Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,8 @@ shared/types/lexicons

# output
.vercel

*storybook.log
storybook-static

.direnv
8 changes: 8 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { StorybookConfig } from '@storybook-vue/nuxt'

const config = {
stories: ['../app/**/*.stories.@(js|ts|mdx)'],
addons: ['@storybook/addon-a11y', '@storybook/addon-docs'],
framework: '@storybook-vue/nuxt',
} satisfies StorybookConfig
export default config
14 changes: 14 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Preview } from '@storybook-vue/nuxt'

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
}

export default preview
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"editor.formatOnSave": true,
"i18n-ally.keystyle": "nested",
"i18n-ally.localesPaths": ["./i18n/locales"],
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": {
"*.vue": "${capture}.stories.ts"
}
}
88 changes: 88 additions & 0 deletions app/components/Input/Base.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { Meta, StoryObj } from '@nuxtjs/storybook'
import { expect, fn, userEvent, within } from '@storybook/test'
import Component from './Base.vue'

const meta = {
component: Component,

argTypes: {
disabled: { control: 'boolean' },
size: {
control: 'select',
options: ['small', 'medium', 'large'],
},
noCorrect: {
control: 'boolean',
description: 'Whether to disable browser autocorrect',
},
onFocus: {
action: 'focus',
description: 'Fired when the input gains focus',
},
onBlur: {
action: 'blur',
description: 'Fired when the input loses focus',
},
},
} satisfies Meta<typeof Component>

export default meta
type Story = StoryObj<typeof meta>

export const Snapshot: Story = {
render: () => ({
template: `
<div style="display: flex; flex-direction: column; gap: 1rem; padding: 1rem;">
<Component size="small" model-value="Small input" />
<Component size="medium" model-value="Medium input" />
<Component size="large" model-value="Large input" />
<Component size="large" model-value="disabled" disabled />
</div>
`,
components: { Component },
}),
}

export const Event: Story = {
args: {
onFocus: fn(),
onBlur: fn(),
},
play: async ({ args, canvasElement }) => {
const canvas = within(canvasElement)
const input = canvas.getByRole('textbox')

await userEvent.click(input)
await expect(args.onFocus).toHaveBeenCalled()

await userEvent.tab()
await expect(args.onBlur).toHaveBeenCalled()
},
}

export const Disable: Story = {
args: { disabled: true },
play: async ({ args, canvasElement }) => {
const canvas = within(canvasElement)
const input = canvas.getByRole('textbox')

await expect(input).toBeDisabled()
await userEvent.click(input)
await expect(args.onFocus).not.toHaveBeenCalled()
},
}

export const NoCorrect: Story = {
args: {
noCorrect: true,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const input = canvas.getByRole('textbox')

await expect(input).toHaveAttribute('autocapitalize', 'off')
await expect(input).toHaveAttribute('autocorrect', 'off')
await expect(input).toHaveAttribute('autocomplete', 'off')
await expect(input).toHaveAttribute('spellcheck', 'false')
},
}
18 changes: 18 additions & 0 deletions app/components/LicenseDisplay.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@nuxtjs/storybook'
import Component from './LicenseDisplay.vue'

const meta = {
component: Component,
tags: ['autodocs'],
argTypes: {
license: {
control: { type: 'text' },
},
},
} satisfies Meta<typeof Component>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = { args: { license: 'MIT' } }
export const Invalid: Story = { args: { license: 'invalid license' } }
39 changes: 39 additions & 0 deletions app/components/OgImage/Default.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { Meta, StoryObj } from '@nuxtjs/storybook'
import Component from './Default.vue'

const meta = {
component: Component,
tags: ['autodocs'],
decorators: [
() => ({
template: '<div style="width:1200px;height:630px; outline:1px solid red"><story/></div>',
}),
],
argTypes: {
primaryColor: {
control: { type: 'color' },
description: 'Primary color used throughout the OG image',
},
title: {
control: { type: 'text' },
description: 'Main title displayed in the OG image',
},
description: {
control: { type: 'text' },
description: 'Description text (supports **bold** markdown)',
},
},
} satisfies Meta<typeof Component>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {}

export const CustomContent: Story = {
args: {
title: 'Custom Package',
description: 'an awesome **customizable** OG image generator',
primaryColor: '#f59e0b',
},
}
18 changes: 18 additions & 0 deletions app/components/OgImage/Package.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@nuxtjs/storybook'
import Component from './Package.vue'

const meta = {
component: Component,
decorators: [
() => ({
template: '<div style="width:1200px;height:630px; outline:1px solid red"><story/></div>',
}),
],
} satisfies Meta<typeof Component>

export default meta
type Story = StoryObj<typeof meta>

export const Package: Story = {
args: { name: 'vue', version: 'latest' },
}
125 changes: 125 additions & 0 deletions app/components/Package/DownloadAnalytics.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import type { Meta, StoryObj } from '@nuxtjs/storybook'
import Component from './DownloadAnalytics.vue'
import type { WeeklyDownloadPoint } from '~/composables/useCharts'

const meta = {
component: Component,
tags: ['autodocs'],
argTypes: {
weeklyDownloads: {
control: { type: 'object' },
description: 'Array of weekly download data points for single package mode',
table: {
type: { summary: 'WeeklyDownloadPoint[]' },
defaultValue: { summary: 'undefined' },
},
},
inModal: {
control: { type: 'boolean' },
description: 'Whether the component is rendered within a modal',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
packageName: {
control: { type: 'text' },
description: 'Package name for single package mode (backward compatible)',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
},
},
packageNames: {
control: { type: 'object' },
description: 'Array of package names for multi-package comparison mode',
table: {
type: { summary: 'string[]' },
defaultValue: { summary: 'undefined' },
},
},
createdIso: {
control: { type: 'text' },
description: 'ISO date string when the package was created (optional)',
table: {
type: { summary: 'string | null' },
defaultValue: { summary: 'null' },
},
},
},
} satisfies Meta<typeof Component>

export default meta
type Story = StoryObj<typeof meta>

// Sample data for single package mode
const sampleWeeklyDownloads: WeeklyDownloadPoint[] = [
{
downloads: 1500,
weekKey: '2024-W01',
weekStart: '2024-01-01',
weekEnd: '2024-01-07',
timestampStart: 1704067200000,
timestampEnd: 1704672000000,
},
{
downloads: 2100,
weekKey: '2024-W02',
weekStart: '2024-01-08',
weekEnd: '2024-01-14',
timestampStart: 1704672000000,
timestampEnd: 1705276800000,
},
{
downloads: 1800,
weekKey: '2024-W03',
weekStart: '2024-01-15',
weekEnd: '2024-01-21',
timestampStart: 1705276800000,
timestampEnd: 1705881600000,
},
{
downloads: 2300,
weekKey: '2024-W04',
weekStart: '2024-01-22',
weekEnd: '2024-01-28',
timestampStart: 1705881600000,
timestampEnd: 1706486400000,
},
{
downloads: 2800,
weekKey: '2024-W05',
weekStart: '2024-01-29',
weekEnd: '2024-02-04',
timestampStart: 1706486400000,
timestampEnd: 1707091200000,
},
]

export const Default: Story = {
args: {},
}

export const SinglePackageWithData: Story = {
args: {
packageName: 'vue',
weeklyDownloads: sampleWeeklyDownloads,
createdIso: '2014-02-25T00:00:00.000Z',
},
}

export const MultiPackageMode: Story = {
args: {
packageNames: ['vue', 'react', 'svelte'],
createdIso: null,
},
}

export const InModal: Story = {
args: {
packageName: 'vue',
weeklyDownloads: sampleWeeklyDownloads,
inModal: true,
createdIso: '2014-02-25T00:00:00.000Z',
},
}
8 changes: 4 additions & 4 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import process from 'node:process'
import { currentLocales } from './config/i18n'
import { isCI, provider } from 'std-env'

const isStorybook = process.env.STORYBOOK === 'true' || process.env.VITEST_STORYBOOK === 'true'

export default defineNuxtConfig({
modules: [
'@unocss/nuxt',
Expand All @@ -14,7 +16,7 @@ export default defineNuxtConfig({
'@vite-pwa/nuxt',
'@vueuse/nuxt',
'@nuxtjs/i18n',
'@nuxtjs/color-mode',
isStorybook ? undefined : '@nuxtjs/color-mode',
],

colorMode: {
Expand Down Expand Up @@ -139,9 +141,7 @@ export default defineNuxtConfig({
},

experimental: {
entryImportMap: false,
typescriptPlugin: true,
viteEnvironmentApi: true,
viteEnvironmentApi: !isStorybook,
typedPages: true,
},

Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
"test:nuxt": "vite test --project nuxt",
"test:types": "pnpm generate:lexicons && nuxt prepare && vue-tsc -b --noEmit && pnpm --filter npmx-connector test:types",
"test:unit": "vite test --project unit",
"start:playwright:webserver": "NODE_ENV=test pnpm preview --port 5678"
"start:playwright:webserver": "NODE_ENV=test pnpm preview --port 5678",
"storybook": "STORYBOOK=true storybook dev -p 6006",
"build-storybook": "STORYBOOK=true storybook build"
},
"dependencies": {
"@atproto/api": "^0.18.17",
Expand Down Expand Up @@ -113,7 +115,11 @@
"@e18e/eslint-plugin": "0.1.4",
"@intlify/core-base": "11.2.8",
"@npm/types": "2.1.0",
"@nuxtjs/storybook": "^9.0.1",
"@playwright/test": "1.58.1",
"@storybook/addon-a11y": "^10.2.7",
"@storybook/addon-docs": "^10.2.7",
"@storybook/test": "8.6.15",
"@types/node": "24.10.9",
"@types/sanitize-html": "2.16.0",
"@types/semver": "7.7.1",
Expand All @@ -132,6 +138,7 @@
"oxlint": "1.42.0",
"schema-dts": "1.1.5",
"simple-git-hooks": "2.13.1",
"storybook": "^10.2.7",
"typescript": "5.9.3",
"vitest": "npm:@voidzero-dev/vite-plus-test@0.0.0-833c515fa25cef20905a7f9affb156dfa6f151ab",
"vitest-environment-nuxt": "1.0.1",
Expand Down
Loading
Loading