Adding storybook and stories while also fixing accessibility in the external link component.

This commit is contained in:
Bradley Shellnut 2025-08-21 17:15:04 -07:00
parent d18f6b0a9f
commit fc4ee0c9ae
20 changed files with 1173 additions and 76 deletions

19
.storybook/main.ts Normal file
View file

@ -0,0 +1,19 @@
import type { StorybookConfig } from '@storybook/sveltekit';
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|ts|svelte)"],
addons: [
"@storybook/addon-svelte-csf",
"@chromatic-com/storybook",
"@storybook/addon-docs",
"@storybook/addon-a11y"
],
framework: {
name: "@storybook/sveltekit",
options: {},
},
core: {
disableTelemetry: true,
},
};
export default config;

16
.storybook/preview.ts Normal file
View file

@ -0,0 +1,16 @@
import '../src/styles/styles.pcss';
import type { Preview } from '@storybook/sveltekit';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;

View file

@ -13,13 +13,19 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "biome lint --error-on-warnings .",
"format": "biome format --write .",
"storybook": "storybook dev -p 6006",
"test:integration": "playwright test",
"test:unit": "vitest"
},
"devDependencies": {
"@biomejs/biome": "^2.1.4",
"@chromatic-com/storybook": "^4.1.1",
"@internationalized/date": "^3.8.2",
"@playwright/test": "^1.54.2",
"@storybook/addon-a11y": "^9.1.3",
"@storybook/addon-docs": "^9.1.3",
"@storybook/addon-svelte-csf": "^5.0.8",
"@storybook/sveltekit": "^9.1.3",
"@sveltejs/enhanced-img": "^0.5.1",
"@sveltejs/kit": "^2.29.0",
"@sveltejs/vite-plugin-svelte": "^5.1.1",
@ -34,6 +40,7 @@
"postcss-preset-env": "^10.2.4",
"satori": "^0.12.2",
"satori-html": "^0.3.2",
"storybook": "^9.1.3",
"svelte": "^5.38.1",
"svelte-check": "^4.3.1",
"svelte-meta-tags": "^4.4.0",

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,18 @@
<script module lang="ts">
import { defineMeta } from "@storybook/addon-svelte-csf";
import Articles from "./Articles.svelte";
const { Story } = defineMeta({
title: "Components/Articles",
component: Articles,
tags: ["autodocs"],
args: {
data: { articles: [], totalArticles: 0, classes: [], compact: false },
},
argTypes: {
data: { control: "object" },
},
});
</script>
<Story name="Default" args={{ data: { articles: [], totalArticles: 0, classes: [], compact: false } }} />

View file

@ -0,0 +1,13 @@
<script context="module" lang="ts">
import { defineMeta } from "@storybook/addon-svelte-csf";
import ArticlesSkeleton from "./ArticlesSkeleton.svelte";
const { Story } = defineMeta({
title: "Components/ArticlesSkeleton",
component: ArticlesSkeleton,
tags: ["autodocs"],
args: { count: 6, classes: [] },
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,38 @@
<script context="module" lang="ts">
import { defineMeta } from "@storybook/addon-svelte-csf";
import Bandcamp from "./Bandcamp.svelte";
const sampleAlbums = [
{
title: "Album One",
artist: "Artist A",
url: "https://example.com",
artwork: "https://picsum.photos/230?1",
src: undefined,
},
{
title: "Album Two",
artist: "Artist B",
url: "https://example.com",
artwork: "https://picsum.photos/230?2",
src: undefined,
},
{
title: "Album Three",
artist: "Artist C",
url: "https://example.com",
artwork: "https://picsum.photos/230?3",
src: undefined,
},
];
const { Story } = defineMeta({
title: "Components/Bandcamp",
component: Bandcamp,
tags: ["autodocs"],
args: { albums: sampleAlbums },
argTypes: { albums: { control: "object" } },
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,40 @@
<script context="module" lang="ts">
import { defineMeta } from "@storybook/addon-svelte-csf";
import ContactHub from "./ContactHub.svelte";
const defaultUserNames = {
x: "example_x",
blueSky: "example.bsky.social",
linkedIn: "example-linkedin",
github: "example-github",
email: "user@example.com",
};
const { Story } = defineMeta({
title: "Components/ContactHub",
component: ContactHub,
tags: ["autodocs"],
args: {
showBlueSky: true,
showEmail: true,
showGithub: true,
showLinkedIn: true,
showX: true,
userNames: defaultUserNames,
showText: true,
justify: true,
},
argTypes: {
showBlueSky: { control: "boolean" },
showEmail: { control: "boolean" },
showGithub: { control: "boolean" },
showLinkedIn: { control: "boolean" },
showX: { control: "boolean" },
showText: { control: "boolean" },
justify: { control: "boolean" },
userNames: { control: "object" },
},
});
</script>
<Story name="Default" />

View file

@ -1,12 +1,12 @@
<script lang="ts">
import { Mail } from "lucide-svelte";
import ExternalLink from "$lib/components/ExternalLink.svelte";
import {
blueSkyIcon,
gitHubIcon,
linkedInIcon,
xIcon,
} from "../util/logoIcons.svelte";
import ExternalLink from '$lib/components/ExternalLink.svelte';
interface Props {
showBlueSky?: boolean;
@ -37,36 +37,66 @@
<div class:justifyCenter={justify}>
{#if showX && userNames?.x}
<ExternalLink
linkData={{ href: `https://www.x.com/${userNames.x}`, ariaLabel: 'Contact through X', title: 'Contact through X', target: '_blank', clazz: "hub-icon x-contact" }}
iconData={{ type: 'svg', icon: xIcon, iconClass: 'center' }}
linkData={{
href: `https://www.x.com/${userNames.x}`,
ariaLabel: "Contact through X",
title: "Contact through X",
target: "_blank",
clazz: "hub-icon x-contact",
}}
iconData={{ type: "svg", icon: xIcon, iconClass: "center" }}
textData={{ showIcon: true }}
/>
{/if}
{#if showBlueSky && userNames?.blueSky}
<ExternalLink
linkData={{ href: `https://bsky.app/profile/${userNames.blueSky}`, ariaLabel: 'Contact through Bluesky', title: 'Contact through Bluesky', target: '_blank', clazz: "hub-icon bluesky-contact" }}
iconData={{ type: 'svg', icon: blueSkyIcon, iconClass: 'center' }}
linkData={{
href: `https://bsky.app/profile/${userNames.blueSky}`,
ariaLabel: "Contact through Bluesky",
title: "Contact through Bluesky",
target: "_blank",
clazz: "hub-icon bluesky-contact",
}}
iconData={{ type: "svg", icon: blueSkyIcon, iconClass: "center" }}
textData={{ showIcon: true }}
/>
{/if}
{#if showLinkedIn && userNames?.linkedIn}
<ExternalLink
linkData={{ href: `https://www.linkedin.com/in/${userNames.linkedIn}`, ariaLabel: 'Contact through LinkedIn', title: 'Contact through LinkedIn', target: '_blank', clazz: "hub-icon linkedIn-contact" }}
iconData={{ type: 'svg', icon: linkedInIcon, iconClass: 'center' }}
linkData={{
href: `https://www.linkedin.com/in/${userNames.linkedIn}`,
ariaLabel: "Contact through LinkedIn",
title: "Contact through LinkedIn",
target: "_blank",
clazz: "hub-icon linkedIn-contact",
}}
iconData={{ type: "svg", icon: linkedInIcon, iconClass: "center" }}
textData={{ showIcon: true }}
/>
{/if}
{#if showGithub && userNames?.github}
<ExternalLink
linkData={{ href: `https://www.github.com/${userNames.github}`, ariaLabel: 'Contact through Github', title: 'Contact through Github', target: '_blank', clazz: "hub-icon github-contact" }}
iconData={{ type: 'svg', icon: gitHubIcon, iconClass: 'center' }}
linkData={{
href: `https://www.github.com/${userNames.github}`,
ariaLabel: "Contact through Github",
title: "Contact through Github",
target: "_blank",
clazz: "hub-icon github-contact",
}}
iconData={{ type: "svg", icon: gitHubIcon, iconClass: "center" }}
textData={{ showIcon: true }}
/>
{/if}
{#if showEmail && userNames?.email}
<ExternalLink
linkData={{ href: `mailto:${userNames.email}`, ariaLabel: 'Contact by email', title: 'Contact by email', target: '_blank', clazz: "hub-icon email-contact" }}
iconData={{ type: 'icon', icon: Mail, iconClass: 'center' }}
linkData={{
href: `mailto:${userNames.email}`,
ariaLabel: "Contact by email",
title: "Contact by email",
target: "_blank",
clazz: "hub-icon email-contact",
}}
iconData={{ type: "icon", icon: Mail, iconClass: "center" }}
textData={{ showIcon: true }}
/>
{/if}

View file

@ -0,0 +1,29 @@
<script module lang="ts">
import { defineMeta } from "@storybook/addon-svelte-csf";
import ExternalLink from "./ExternalLink.svelte";
import { blueSkyIcon } from "../util/logoIcons.svelte";
const { Story } = defineMeta({
title: "Components/ExternalLink",
component: ExternalLink,
tags: ["autodocs"],
args: {
linkData: {
href: "https://example.com",
ariaLabel: "Go to example.com",
title: "Example",
target: "_blank",
clazz: "hub-icon bluesky-contact",
},
textData: { text: "Visit Example", location: "left", showIcon: true },
iconData: { type: "svg", icon: blueSkyIcon, iconClass: "center" },
},
argTypes: {
linkData: { control: "object" },
textData: { control: "object" },
iconData: { control: "object" },
},
});
</script>
<Story name="Default" />

View file

@ -52,14 +52,15 @@
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>{linkData?.title ?? `Open ${linkData?.ariaLabel} externally`}</title>
{@render (icon as any)()}
</svg>
{:else if type === "icon" && icon}
{@const Icon = icon}
<Icon />
<Icon><title>{linkData?.title ?? `Open ${linkData?.ariaLabel} externally`}</title></Icon>
{:else}
{@const Icon = ExternalLink}
<Icon />
<Icon><title>{linkData?.title ?? `Open ${linkData?.ariaLabel} externally`}</title></Icon>
{/if}
{/snippet}

View file

@ -0,0 +1,29 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import LazyImage from './LazyImage.svelte';
const sampleSrc = {
img: { src: 'https://picsum.photos/400/300', w: 400, h: 300 },
sources: {
avif: 'https://picsum.photos/400/300.avif',
webp: 'https://picsum.photos/400/300.webp',
jpg: 'https://picsum.photos/400/300.jpg'
}
};
const { Story } = defineMeta({
title: 'Components/LazyImage',
component: LazyImage,
tags: ['autodocs'],
args: { clazz: 'demo-image', src: sampleSrc, alt: 'Random image', style: '', loading: 'lazy' },
argTypes: {
clazz: { control: 'text' },
src: { control: 'object' },
alt: { control: 'text' },
style: { control: 'text' },
loading: { control: { type: 'select' }, options: ['lazy', 'eager'] }
}
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,16 @@
<script module lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Link from './Link.svelte';
const { Story } = defineMeta({
title: 'Components/Link',
component: Link,
tags: ['autodocs']
});
</script>
<Story name="Default">
<Link href="/" ariaLabel="Example link">
<span>Go Home</span>
</Link>
</Story>

View file

@ -0,0 +1,20 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Pagination from './Pagination.svelte';
const { Story } = defineMeta({
title: 'Components/Pagination',
component: Pagination,
tags: ['autodocs'],
args: { additionalClasses: '', pageSize: 10, totalCount: 50, currentPage: 1, base: '/page' },
argTypes: {
additionalClasses: { control: 'text' },
pageSize: { control: { type: 'number', min: 1, step: 1 } },
totalCount: { control: { type: 'number', min: 0, step: 1 } },
currentPage: { control: { type: 'number', min: 1, step: 1 } },
base: { control: 'text' }
}
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,14 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Tag from './Tag.svelte';
const { Story } = defineMeta({
title: 'Components/Tag',
component: Tag,
tags: ['autodocs'],
args: { name: 'JavaScript' },
argTypes: { name: { control: 'text' } }
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,12 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Footer from './index.svelte';
const { Story } = defineMeta({
title: 'Components/Footer',
component: Footer,
tags: ['autodocs']
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,12 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Header from './index.svelte';
const { Story } = defineMeta({
title: 'Components/Header',
component: Header,
tags: ['autodocs']
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,12 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Logo from './index.svelte';
const { Story } = defineMeta({
title: 'Components/Logo',
component: Logo,
tags: ['autodocs']
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,12 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import Nav from './index.svelte';
const { Story } = defineMeta({
title: 'Components/Nav',
component: Nav,
tags: ['autodocs']
});
</script>
<Story name="Default" />

View file

@ -0,0 +1,30 @@
<script context="module" lang="ts">
import { defineMeta } from '@storybook/addon-svelte-csf';
import SocialImageCard from './socialImageCard.svelte';
const { Story } = defineMeta({
title: 'Components/SocialImageCard',
component: SocialImageCard,
tags: ['autodocs'],
args: {
header: 'Bradley Shellnut',
page: 'Home',
image: '/b_shell_nut_favicon.png',
content: 'Welcome to my site',
width: 800,
height: 418,
url: 'https://bradleyshellnut.com'
},
argTypes: {
header: { control: 'text' },
page: { control: 'text' },
image: { control: 'text' },
content: { control: 'text' },
width: { control: { type: 'number', min: 100, step: 10 } },
height: { control: { type: 'number', min: 100, step: 10 } },
url: { control: 'text' }
}
});
</script>
<Story name="Default" />