chore(shared/ui): enhance stories with cases, controls and documentation
All checks were successful
Workflow / build (pull_request) Successful in 52s

This commit is contained in:
Ilia Mashkov
2026-01-18 20:55:36 +03:00
parent e7f4304391
commit c0eed67618
5 changed files with 295 additions and 13 deletions

View File

@@ -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',
},
},
});
</script>
<script lang="ts">
const filter = createFilter({
const defaultFilter = createFilter({
properties: [{
id: 'cats',
name: 'Cats',
@@ -30,8 +44,30 @@ const filter = createFilter({
value: 'birds',
}],
});
const selectedFilter = createFilter({
properties: [{
id: 'rice',
name: 'Rice',
value: 'rice',
selected: true,
}, {
id: 'beans',
name: 'Beans',
value: 'beans',
selected: true,
}, {
id: 'potatoes',
name: 'Potatoes',
value: 'potatoes',
selected: true,
}],
});
</script>
<Story name="Default">
<CheckboxFilter {filter} displayedLabel="Zoo" />
<CheckboxFilter filter={defaultFilter} displayedLabel="Zoo" />
</Story>
<Story name="Selected">
<CheckboxFilter filter={selectedFilter} displayedLabel="Shopping list" />
</Story>

View File

@@ -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)',
},
},
});
</script>
@@ -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 });
</script>
<Story name="Default">
<Story
name="Default"
args={{
control: defaultControl,
}}
>
<ComboControl control={defaultControl} />
</Story>
<Story name="At Minimum">
<Story
name="At Minimum"
args={{
control: atMinimumControl,
}}
>
<ComboControl control={atMinimumControl} />
</Story>
<Story name="At Maximum">
<Story
name="At Maximum"
args={{
control: atMaximumControl,
}}
>
<ComboControl control={atMaximumControl} />
</Story>
<Story name="With Float">
<Story
name="With Float"
args={{
control: withFloatControl,
}}
>
<ComboControl control={withFloatControl} />
</Story>
<Story
name="Custom Labels"
args={{
control: customLabelsControl,
decreaseLabel: 'Decrease font size',
increaseLabel: 'Increase font size',
controlLabel: 'Open font size controls',
}}
>
<ComboControl
control={customLabelsControl}
decreaseLabel="Decrease font size"
increaseLabel="Increase font size"
controlLabel="Open font size controls"
/>
</Story>

View File

@@ -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',
},
},
});
</script>
<script lang="ts">
let value = $state('Here we can type and edit the content. Try it!');
let smallValue = $state('Small font size for compact text.');
let largeValue = $state('Large font size for emphasis.');
let spacedValue = $state('Wide letter spacing.');
let longValue = $state(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
);
</script>
<Story name="Default">
<Story
name="Default"
args={{
text: value,
fontSize: 48,
lineHeight: 1.2,
letterSpacing: 0,
}}
>
<ContentEditable bind:text={value} />
</Story>
<Story
name="Small Font"
args={{
text: smallValue,
fontSize: 16,
lineHeight: 1.5,
letterSpacing: 0,
}}
>
<ContentEditable bind:text={smallValue} />
</Story>
<Story
name="Large Font"
args={{
text: largeValue,
fontSize: 72,
lineHeight: 1.1,
letterSpacing: 0,
}}
>
<ContentEditable bind:text={largeValue} />
</Story>
<Story
name="Wide Letter Spacing"
args={{
text: spacedValue,
fontSize: 32,
lineHeight: 1.3,
letterSpacing: 0.3,
}}
>
<ContentEditable bind:text={spacedValue} />
</Story>
<Story
name="Long Text"
args={{
text: longValue,
fontSize: 24,
lineHeight: 1.6,
letterSpacing: 0,
}}
>
<ContentEditable bind:text={longValue} />
</Story>

View File

@@ -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',
},
},
});
</script>
<script lang="ts">
let value = $state('');
let defaultSearchValue = $state('');
let withLabelValue = $state('');
let noChildrenValue = $state('');
</script>
<Story name="Default">
<SearchBar bind:value={value} placeholder="Type here...">
<Story
name="Default"
args={{
value: defaultSearchValue,
placeholder: 'Type here...',
}}
>
<SearchBar bind:value={defaultSearchValue} placeholder="Type here...">
Here will be the search result
<br />
Popover closes only when the user clicks outside the search bar or presses the Escape key.
</SearchBar>
</Story>
<Story
name="With Label"
args={{
value: withLabelValue,
placeholder: 'Search products...',
label: 'Search',
}}
>
<SearchBar bind:value={withLabelValue} placeholder="Search products..." label="Search">
<div class="p-4">
<p class="text-sm text-muted-foreground">No results found</p>
</div>
</SearchBar>
</Story>
<Story
name="Minimal Content"
args={{
value: noChildrenValue,
placeholder: 'Quick search...',
}}
>
<SearchBar bind:value={noChildrenValue} placeholder="Quick search...">
<div class="p-4 text-center text-sm text-muted-foreground">
Start typing to see results
</div>
</SearchBar>
</Story>

View File

@@ -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',
},
},
});
</script>
<script lang="ts">
const items = Array.from({ length: 10000 }, (_, i) => `${i + 1}) I will not waste chalk.`);
const smallDataSet = Array.from({ length: 20 }, (_, i) => `${i + 1}) I will not waste chalk.`);
const largeDataSet = Array.from(
{ length: 10000 },
(_, i) => `${i + 1}) I will not skateboard in the halls.`,
);
const emptyDataSet: string[] = [];
</script>
<Story name="Default">
<VirtualList items={items} itemHeight={40}>
<Story name="Small Dataset">
<VirtualList items={smallDataSet} itemHeight={40}>
{#snippet children({ item })}
<div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div>
{/snippet}
</VirtualList>
</Story>
<Story name="Large Dataset">
<VirtualList items={largeDataSet} itemHeight={40}>
{#snippet children({ item })}
<div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div>
{/snippet}
</VirtualList>
</Story>
<Story name="Empty Dataset">
<VirtualList items={emptyDataSet} itemHeight={40}>
{#snippet children({ item })}
<div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div>
{/snippet}