···11-import type { Preview } from '@storybook/sveltekit'
22-import '../src/routes/layout.css'
11+import type { Preview } from '@storybook/sveltekit';
22+import '../src/routes/layout.css';
3344const preview: Preview = {
55- parameters: {
66- controls: {
77- matchers: {
88- color: /(background|color)$/i,
99- date: /Date$/i,
1010- },
1111- },
55+ parameters: {
66+ controls: {
77+ matchers: {
88+ color: /(background|color)$/i,
99+ date: /Date$/i
1010+ }
1111+ },
12121313- a11y: {
1414- // 'todo' - show a11y violations in the test UI only
1515- // 'error' - fail CI on a11y violations
1616- // 'off' - skip a11y checks entirely
1717- test: 'todo'
1818- }
1919- },
1313+ a11y: {
1414+ // 'todo' - show a11y violations in the test UI only
1515+ // 'error' - fail CI on a11y violations
1616+ // 'off' - skip a11y checks entirely
1717+ test: 'todo'
1818+ }
1919+ }
2020};
21212222-export default preview;2222+export default preview;
+2-2
.storybook/vitest.setup.ts
···11-import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
11+import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';
22import { setProjectAnnotations } from '@storybook/sveltekit';
33import * as projectAnnotations from './preview';
4455// This is an important step to apply the right configuration when testing your stories.
66// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
77-setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);77+setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
···22 import Post from '$lib/components/Post.svelte';
3344 let { data } = $props();
55-65</script>
7687{#each data.data.feed as entry, i (i)}
99- <Post post={entry.post} />
88+ <Post post={entry.post} />
109{/each}
1111-
-37
src/routes/feed/[[aturl]]/+layout.js
···11-import { publicClient, rpc } from '$lib/atproto';
22-import { resumeSession, getProfile } from '$lib/atproto';
33-44-export async function load() {
55- await resumeSession();
66- const client = rpc;
77- if (!client) {
88- throw new Error(`authenticated client not loaded`);
99- }
1010- const { data, ok } = await client.get('app.bsky.actor.getPreferences', {
1111- params: {}
1212- });
1313- if (!ok) {
1414- throw new Error(`couldn't load preferences`);
1515- }
1616- // extract the savedFeedsPrefV2 from this
1717- const savedFeedsPrefV2 = data.preferences.find(x => x.$type === "app.bsky.actor.defs#savedFeedsPrefV2");
1818-1919- if (savedFeedsPrefV2?.items) {
2020- const pinnedItems = savedFeedsPrefV2?.items.filter(x => x.pinned && x.type === 'feed')
2121- const { data, ok } = await client.get('app.bsky.feed.getFeedGenerators', {
2222- params: {
2323- feeds: /** @type {import('@atcute/lexicons').ResourceUri[]} */ (pinnedItems.map(x => x.value))
2424- }
2525- })
2626-2727- if (!ok) {
2828- throw new Error(`failed to get list of feeds`);
2929- }
3030-3131- const feedMap = Object.fromEntries(data.feeds.map(item => [item.uri, item]));
3232- console.log(feedMap)
3333- return { items: savedFeedsPrefV2.items, feedMap };
3434- }
3535-3636- return { items: [], feedMap: {} };
3737-}
···11<script module>
22- import { defineMeta } from '@storybook/addon-svelte-csf';
33- import Button from './Button.svelte';
44- import { fn } from 'storybook/test';
22+ import { defineMeta } from '@storybook/addon-svelte-csf';
33+ import Button from './Button.svelte';
44+ import { fn } from 'storybook/test';
5566- // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
77- const { Story } = defineMeta({
88- title: 'Example/Button',
99- component: Button,
1010- tags: ['autodocs'],
1111- argTypes: {
1212- backgroundColor: { control: 'color' },
1313- size: {
1414- control: { type: 'select' },
1515- options: ['small', 'medium', 'large'],
1616- },
1717- },
1818- args: {
1919- onclick: fn(),
2020- }
2121- });
66+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
77+ const { Story } = defineMeta({
88+ title: 'Example/Button',
99+ component: Button,
1010+ tags: ['autodocs'],
1111+ argTypes: {
1212+ backgroundColor: { control: 'color' },
1313+ size: {
1414+ control: { type: 'select' },
1515+ options: ['small', 'medium', 'large']
1616+ }
1717+ },
1818+ args: {
1919+ onclick: fn()
2020+ }
2121+ });
2222</script>
23232424<!-- More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -->
+22-22
src/stories/Button.svelte
···11<script lang="ts">
22- import './button.css';
22+ import './button.css';
3344- interface Props {
55- /** Is this the principal call to action on the page? */
66- primary?: boolean;
77- /** What background color to use */
88- backgroundColor?: string;
99- /** How large should the button be? */
1010- size?: 'small' | 'medium' | 'large';
1111- /** Button contents */
1212- label: string;
1313- /** The onclick event handler */
1414- onclick?: () => void;
1515- }
44+ interface Props {
55+ /** Is this the principal call to action on the page? */
66+ primary?: boolean;
77+ /** What background color to use */
88+ backgroundColor?: string;
99+ /** How large should the button be? */
1010+ size?: 'small' | 'medium' | 'large';
1111+ /** Button contents */
1212+ label: string;
1313+ /** The onclick event handler */
1414+ onclick?: () => void;
1515+ }
1616+1717+ const { primary = false, backgroundColor, size = 'medium', label, ...props }: Props = $props();
16181717- const { primary = false, backgroundColor, size = 'medium', label, ...props }: Props = $props();
1818-1919- let mode = $derived(primary ? 'storybook-button--primary' : 'storybook-button--secondary');
2020- let style = $derived(backgroundColor ? `background-color: ${backgroundColor}` : '');
1919+ let mode = $derived(primary ? 'storybook-button--primary' : 'storybook-button--secondary');
2020+ let style = $derived(backgroundColor ? `background-color: ${backgroundColor}` : '');
2121</script>
22222323<button
2424- type="button"
2525- class={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
2626- {style}
2727- {...props}
2424+ type="button"
2525+ class={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
2626+ {style}
2727+ {...props}
2828>
2929- {label}
2929+ {label}
3030</button>
+36-31
src/stories/Configure.mdx
···11-import { Meta } from "@storybook/addon-docs/blocks";
11+import { Meta } from '@storybook/addon-docs/blocks';
2233-import Github from "./assets/github.svg";
44-import Discord from "./assets/discord.svg";
55-import Youtube from "./assets/youtube.svg";
66-import Tutorials from "./assets/tutorials.svg";
77-import Styling from "./assets/styling.png";
88-import Context from "./assets/context.png";
99-import Assets from "./assets/assets.png";
1010-import Docs from "./assets/docs.png";
1111-import Share from "./assets/share.png";
1212-import FigmaPlugin from "./assets/figma-plugin.png";
1313-import Testing from "./assets/testing.png";
1414-import Accessibility from "./assets/accessibility.png";
1515-import Theming from "./assets/theming.png";
1616-import AddonLibrary from "./assets/addon-library.png";
33+import Github from './assets/github.svg';
44+import Discord from './assets/discord.svg';
55+import Youtube from './assets/youtube.svg';
66+import Tutorials from './assets/tutorials.svg';
77+import Styling from './assets/styling.png';
88+import Context from './assets/context.png';
99+import Assets from './assets/assets.png';
1010+import Docs from './assets/docs.png';
1111+import Share from './assets/share.png';
1212+import FigmaPlugin from './assets/figma-plugin.png';
1313+import Testing from './assets/testing.png';
1414+import Accessibility from './assets/accessibility.png';
1515+import Theming from './assets/theming.png';
1616+import AddonLibrary from './assets/addon-library.png';
17171818-export const RightArrow = () => <svg
1919- viewBox="0 0 14 14"
2020- width="8px"
2121- height="14px"
2222- style={{
2323- marginLeft: '4px',
2424- display: 'inline-block',
2525- shapeRendering: 'inherit',
2626- verticalAlign: 'middle',
2727- fill: 'currentColor',
2828- 'path fill': 'currentColor'
2929- }}
3030->
3131- <path d="m11.1 7.35-5.5 5.5a.5.5 0 0 1-.7-.7L10.04 7 4.9 1.85a.5.5 0 1 1 .7-.7l5.5 5.5c.2.2.2.5 0 .7Z" />
3232-</svg>
1818+export const RightArrow = () => (
1919+ <svg
2020+ viewBox="0 0 14 14"
2121+ width="8px"
2222+ height="14px"
2323+ style={{
2424+ marginLeft: '4px',
2525+ display: 'inline-block',
2626+ shapeRendering: 'inherit',
2727+ verticalAlign: 'middle',
2828+ fill: 'currentColor',
2929+ 'path fill': 'currentColor'
3030+ }}
3131+ >
3232+ <path d="m11.1 7.35-5.5 5.5a.5.5 0 0 1-.7-.7L10.04 7 4.9 1.85a.5.5 0 1 1 .7-.7l5.5 5.5c.2.2.2.5 0 .7Z" />
3333+ </svg>
3434+);
33353436<Meta title="Configure your project" />
3537···3840 # Configure your project
39414042 Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community.
4343+4144 </div>
4245 <div className="sb-section">
4346 <div className="sb-section-item">
···8487 # Do more with Storybook
85888689 Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs.
9090+8791 </div>
88928993 <div className="sb-section">
···203207 target="_blank"
204208 >Discover tutorials<RightArrow /></a>
205209 </div>
210210+206211</div>
207212208213<style>
209209- {`
214214+ {`
210215 .sb-container {
211216 margin-bottom: 48px;
212217 }
+19-19
src/stories/Header.stories.svelte
···11<script module>
22- import { defineMeta } from '@storybook/addon-svelte-csf';
33- import Header from './Header.svelte';
44- import { fn } from 'storybook/test';
22+ import { defineMeta } from '@storybook/addon-svelte-csf';
33+ import Header from './Header.svelte';
44+ import { fn } from 'storybook/test';
5566- // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
77- const { Story } = defineMeta({
88- title: 'Example/Header',
99- component: Header,
1010- // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
1111- tags: ['autodocs'],
1212- parameters: {
1313- // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
1414- layout: 'fullscreen',
1515- },
1616- args: {
1717- onLogin: fn(),
1818- onLogout: fn(),
1919- onCreateAccount: fn(),
2020- }
2121- });
66+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
77+ const { Story } = defineMeta({
88+ title: 'Example/Header',
99+ component: Header,
1010+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
1111+ tags: ['autodocs'],
1212+ parameters: {
1313+ // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
1414+ layout: 'fullscreen'
1515+ },
1616+ args: {
1717+ onLogin: fn(),
1818+ onLogout: fn(),
1919+ onCreateAccount: fn()
2020+ }
2121+ });
2222</script>
23232424<Story name="Logged In" args={{ user: { name: 'Jane Doe' } }} />
···11<script module>
22- import { defineMeta } from '@storybook/addon-svelte-csf';
33- import { expect, userEvent, waitFor, within } from 'storybook/test';
44- import Page from './Page.svelte';
55- import { fn } from 'storybook/test';
22+ import { defineMeta } from '@storybook/addon-svelte-csf';
33+ import { expect, userEvent, waitFor, within } from 'storybook/test';
44+ import Page from './Page.svelte';
55+ import { fn } from 'storybook/test';
6677- // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
88- const { Story } = defineMeta({
99- title: 'Example/Page',
1010- component: Page,
1111- parameters: {
1212- // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
1313- layout: 'fullscreen',
1414- },
1515- });
77+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories
88+ const { Story } = defineMeta({
99+ title: 'Example/Page',
1010+ component: Page,
1111+ parameters: {
1212+ // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
1313+ layout: 'fullscreen'
1414+ }
1515+ });
1616</script>
17171818-<Story name="Logged In" play={async ({ canvasElement }) => {
1919- const canvas = within(canvasElement);
2020- const loginButton = canvas.getByRole('button', { name: /Log in/i });
2121- await expect(loginButton).toBeInTheDocument();
2222- await userEvent.click(loginButton);
2323- await waitFor(() => expect(loginButton).not.toBeInTheDocument());
1818+<Story
1919+ name="Logged In"
2020+ play={async ({ canvasElement }) => {
2121+ const canvas = within(canvasElement);
2222+ const loginButton = canvas.getByRole('button', { name: /Log in/i });
2323+ await expect(loginButton).toBeInTheDocument();
2424+ await userEvent.click(loginButton);
2525+ await waitFor(() => expect(loginButton).not.toBeInTheDocument());
24262525- const logoutButton = canvas.getByRole('button', { name: /Log out/i });
2626- await expect(logoutButton).toBeInTheDocument();
2727- }}
2727+ const logoutButton = canvas.getByRole('button', { name: /Log out/i });
2828+ await expect(logoutButton).toBeInTheDocument();
2929+ }}
2830/>
29313032<Story name="Logged Out" />
+61-61
src/stories/Page.svelte
···11<script lang="ts">
22- import './page.css';
33- import Header from './Header.svelte';
22+ import './page.css';
33+ import Header from './Header.svelte';
4455- let user = $state<{ name: string }>();
55+ let user = $state<{ name: string }>();
66</script>
7788<article>
99- <Header
1010- {user}
1111- onLogin={() => (user = { name: 'Jane Doe' })}
1212- onLogout={() => (user = undefined)}
1313- onCreateAccount={() => (user = { name: 'Jane Doe' })}
1414- />
99+ <Header
1010+ {user}
1111+ onLogin={() => (user = { name: 'Jane Doe' })}
1212+ onLogout={() => (user = undefined)}
1313+ onCreateAccount={() => (user = { name: 'Jane Doe' })}
1414+ />
15151616- <section class="storybook-page">
1717- <h2>Pages in Storybook</h2>
1818- <p>
1919- We recommend building UIs with a
2020- <a
2121- href="https://blog.hichroma.com/component-driven-development-ce1109d56c8e"
2222- target="_blank"
2323- rel="noopener noreferrer"
2424- >
2525- <strong>component-driven</strong>
2626- </a>
2727- process starting with atomic components and ending with pages.
2828- </p>
2929- <p>
3030- Render pages with mock data. This makes it easy to build and review page states without
3131- needing to navigate to them in your app. Here are some handy patterns for managing page data
3232- in Storybook:
3333- </p>
3434- <ul>
3535- <li>
3636- Use a higher-level connected component. Storybook helps you compose such data from the
3737- "args" of child component stories
3838- </li>
3939- <li>
4040- Assemble data in the page component from your services. You can mock these services out
4141- using Storybook.
4242- </li>
4343- </ul>
4444- <p>
4545- Get a guided tutorial on component-driven development at
4646- <a href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
4747- Storybook tutorials
4848- </a>
4949- . Read more in the
5050- <a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">docs</a>
5151- .
5252- </p>
5353- <div class="tip-wrapper">
5454- <span class="tip">Tip</span>
5555- Adjust the width of the canvas with the
5656- <svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
5757- <g fill="none" fill-rule="evenodd">
5858- <path
5959- d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0
1616+ <section class="storybook-page">
1717+ <h2>Pages in Storybook</h2>
1818+ <p>
1919+ We recommend building UIs with a
2020+ <a
2121+ href="https://blog.hichroma.com/component-driven-development-ce1109d56c8e"
2222+ target="_blank"
2323+ rel="noopener noreferrer"
2424+ >
2525+ <strong>component-driven</strong>
2626+ </a>
2727+ process starting with atomic components and ending with pages.
2828+ </p>
2929+ <p>
3030+ Render pages with mock data. This makes it easy to build and review page states without
3131+ needing to navigate to them in your app. Here are some handy patterns for managing page data
3232+ in Storybook:
3333+ </p>
3434+ <ul>
3535+ <li>
3636+ Use a higher-level connected component. Storybook helps you compose such data from the
3737+ "args" of child component stories
3838+ </li>
3939+ <li>
4040+ Assemble data in the page component from your services. You can mock these services out
4141+ using Storybook.
4242+ </li>
4343+ </ul>
4444+ <p>
4545+ Get a guided tutorial on component-driven development at
4646+ <a href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
4747+ Storybook tutorials
4848+ </a>
4949+ . Read more in the
5050+ <a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">docs</a>
5151+ .
5252+ </p>
5353+ <div class="tip-wrapper">
5454+ <span class="tip">Tip</span>
5555+ Adjust the width of the canvas with the
5656+ <svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
5757+ <g fill="none" fill-rule="evenodd">
5858+ <path
5959+ d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0
6060 01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0
6161 010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
6262- id="a"
6363- fill="#999"
6464- />
6565- </g>
6666- </svg>
6767- Viewports addon in the toolbar
6868- </div>
6969- </section>
6262+ id="a"
6363+ fill="#999"
6464+ />
6565+ </g>
6666+ </svg>
6767+ Viewports addon in the toolbar
6868+ </div>
6969+ </section>
7070</article>