Files
frontend-svelte/src/entities/Font/ui/FontSampler/FontSampler.stories.svelte
T
Ilia Mashkov 09869aed00 refactor(entities/Font): relocate FontSampler from DisplayFont, invert typography
DisplayFont was not a feature (FSD+ A-6): the whole slice was one
presentational component that renders a Font styled by typography, with no
model/domain/action. To get typography it reached sideways into a sibling
feature (`$features/AdjustTypography/model`) — a feature->feature edge
(C-1), the symptom of the mislayering, not the disease.

Fix by inversion, mirroring the existing `status` prop pattern:

- move FontSampler into entities/Font/ui (it now uses only entity siblings
  + $shared/ui)
- it accepts a `typography` prop typed to a minimal contract defined in the
  component; the AdjustTypography store satisfies it structurally, so the
  entity has no dependency on the feature
- SampleList (owns both) injects its typographySettingsStore as the prop
- delete the DisplayFont slice; export FontSampler from the Font barrel;
  relocate the story (now passes a mock typography)

Resolves A-6, A-7, and the FontSampler half of C-1. Verified: 0 type
errors, 0 lint (boundary rule satisfied), 905 unit + 213 component tests,
production build OK.
2026-06-03 08:34:49 +03:00

134 lines
3.5 KiB
Svelte

<script module>
import Providers from '$shared/lib/storybook/Providers.svelte';
import { defineMeta } from '@storybook/addon-svelte-csf';
import FontSampler from './FontSampler.svelte';
const { Story } = defineMeta({
title: 'Entities/Font/FontSampler',
component: FontSampler,
tags: ['autodocs'],
parameters: {
docs: {
description: {
component:
'Displays a sample text with a given font in a contenteditable element. Visual design matches FontCard: sharp corners, brand hover accent, header stats showing font properties (size, weight, line height, letter spacing). Staggered entrance animation based on index.',
},
story: { inline: false },
},
},
argTypes: {
font: {
control: 'object',
description: 'Font information object',
},
status: {
control: 'select',
options: ['loading', 'loaded', 'error'],
description: 'Font-load status, supplied by the composing widget and forwarded to FontApplicator',
},
text: {
control: 'text',
description: 'Editable sample text (two-way bindable)',
},
index: {
control: { type: 'number', min: 0 },
description: 'Position index — drives the staggered entrance delay',
},
},
});
</script>
<script lang="ts">
import type { ComponentProps } from 'svelte';
import type { UnifiedFont } from '../../model/types';
// Mock fonts for testing
const mockArial: UnifiedFont = {
id: 'arial',
name: 'Arial',
provider: 'google',
category: 'sans-serif',
subsets: ['latin'],
variants: ['400', '700'],
styles: {
regular: '',
bold: '',
},
metadata: {
cachedAt: Date.now(),
version: '1.0',
popularity: 1,
},
features: {
isVariable: false,
},
};
const mockGeorgia: UnifiedFont = {
id: 'georgia',
name: 'Georgia',
provider: 'google',
category: 'serif',
subsets: ['latin'],
variants: ['400', '700'],
styles: {
regular: '',
bold: '',
},
metadata: {
cachedAt: Date.now(),
version: '1.0',
popularity: 2,
},
features: {
isVariable: false,
},
};
// Stand-in for the AdjustTypography store the composing widget injects.
const mockTypography = {
renderedSize: 48,
weight: 400,
height: 1.5,
spacing: 0,
};
</script>
<Story
name="Default"
args={{
font: mockArial,
status: 'loaded',
text: 'The quick brown fox jumps over the lazy dog',
index: 0,
typography: mockTypography,
}}
>
{#snippet template(args: ComponentProps<typeof FontSampler>)}
<Providers>
<div class="max-w-2xl mx-auto">
<FontSampler {...args} />
</div>
</Providers>
{/snippet}
</Story>
<Story
name="Long Text"
args={{
font: mockGeorgia,
status: 'loaded',
text:
'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.',
index: 1,
typography: mockTypography,
}}
>
{#snippet template(args: ComponentProps<typeof FontSampler>)}
<Providers>
<div class="max-w-2xl mx-auto">
<FontSampler {...args} />
</div>
</Providers>
{/snippet}
</Story>