From 3e4ec12b22e064419c3a74394de23e9f7d757c1b Mon Sep 17 00:00:00 2001 From: cylewaitforit Date: Thu, 5 Mar 2026 08:47:22 -0600 Subject: [PATCH 1/8] docs(ui): add ButtonGroup stories to storybook --- .storybook/main.ts | 35 +---- .storybook/manager.ts | 9 +- .storybook/preview-head.html | 6 + .storybook/preview.ts | 57 ++++---- .storybook/theme.ts | 13 ++ app/components/Button/Base.stories.ts | 60 --------- app/components/Button/Base.vue | 7 + app/components/Button/Button.stories.ts | 165 ++++++++++++++++++++++++ package.json | 1 + pnpm-lock.yaml | 31 +++++ pnpm-workspace.yaml | 1 + 11 files changed, 264 insertions(+), 121 deletions(-) create mode 100644 .storybook/preview-head.html create mode 100644 .storybook/theme.ts delete mode 100644 app/components/Button/Base.stories.ts create mode 100644 app/components/Button/Button.stories.ts diff --git a/.storybook/main.ts b/.storybook/main.ts index 45af6e1491..06e3153da9 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -2,7 +2,12 @@ import type { StorybookConfig } from '@storybook-vue/nuxt' const config = { stories: ['../app/**/*.stories.@(js|ts)'], - addons: ['@storybook/addon-a11y', '@storybook/addon-docs', '@storybook/addon-themes'], + addons: [ + '@storybook/addon-a11y', + '@storybook/addon-docs', + '@storybook/addon-themes', + 'storybook-i18n', + ], framework: '@storybook-vue/nuxt', staticDirs: ['./.public'], features: { @@ -19,34 +24,6 @@ const config = { } }, }) - // Replace the built-in vue-docgen plugin with a fault-tolerant version. - // vue-docgen-api can crash on components that import types from other - // .vue files (it tries to parse the SFC with @babel/parser as plain TS). - // This wrapper catches those errors so the build doesn't fail. - const docgenPlugin = config.plugins?.find( - (p): p is Extract => - !!p && typeof p === 'object' && 'name' in p && p.name === 'storybook:vue-docgen-plugin', - ) - - if (docgenPlugin && 'transform' in docgenPlugin) { - const hook = docgenPlugin.transform - // Vite plugin hooks can be a function or an object with a `handler` property - const originalFn = typeof hook === 'function' ? hook : hook?.handler - if (originalFn) { - const wrapped = async function (this: unknown, ...args: unknown[]) { - try { - return await originalFn.apply(this, args) - } catch { - return undefined - } - } - if (typeof hook === 'function') { - docgenPlugin.transform = wrapped as typeof hook - } else if (hook) { - hook.handler = wrapped as typeof hook.handler - } - } - } return config }, diff --git a/.storybook/manager.ts b/.storybook/manager.ts index 023e4fd21d..e56c80a6a6 100644 --- a/.storybook/manager.ts +++ b/.storybook/manager.ts @@ -1,11 +1,6 @@ import { addons } from 'storybook/manager-api' -import { create } from 'storybook/theming' - -const npmxTheme = create({ - brandTitle: 'npmx Storybook', - brandImage: '/npmx-storybook.svg', -}) +import npmxDark from './theme' addons.setConfig({ - theme: npmxTheme, + theme: npmxDark, }) diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 0000000000..49c5636054 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,6 @@ + diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 3242f7788a..1a3f4ff432 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,8 +1,10 @@ import type { Preview } from '@storybook-vue/nuxt' import { withThemeByDataAttribute } from '@storybook/addon-themes' +import { addons } from 'storybook/preview-api' import { currentLocales } from '../config/i18n' import { fn } from 'storybook/test' import { ACCENT_COLORS } from '../shared/utils/constants' +import npmxDark from './theme' // related: https://github.com/npmx-dev/npmx.dev/blob/1431d24be555bca5e1ae6264434d49ca15173c43/test/nuxt/setup.ts#L12-L26 // Stub Nuxt specific globals @@ -25,25 +27,22 @@ const preview: Preview = { date: /Date$/i, }, }, + docs: { + theme: npmxDark, + }, + }, + initialGlobals: { + locale: 'en-US', + locales: currentLocales.reduce( + (acc, locale) => { + acc[locale.code] = locale.name + return acc + }, + {} as Record, + ), }, // Provides toolbars to switch things like theming and language globalTypes: { - locale: { - name: 'Locale', - description: 'UI language', - defaultValue: 'en-US', - toolbar: { - icon: 'globe', - dynamicTitle: true, - items: [ - // English is at the top so it's easier to reset to it - { value: 'en-US', title: 'English (US)' }, - ...currentLocales - .filter(locale => locale.code !== 'en-US') - .map(locale => ({ value: locale.code, title: locale.name })), - ], - }, - }, accentColor: { name: 'Accent Color', description: 'Accent color', @@ -70,9 +69,9 @@ const preview: Preview = { attributeName: 'data-theme', }), (story, context) => { - const { locale, accentColor } = context.globals as { - locale: string + const { accentColor, locale } = context.globals as { accentColor?: string + locale?: string } // Set accent color from globals @@ -82,16 +81,24 @@ const preview: Preview = { document.documentElement.style.removeProperty('--accent-color') } + // Store reference to i18n instance for locale changes + let i18nInstance: any = null + + // Subscribe to locale changes from storybook-i18n addon + addons.getChannel().on('LOCALE_CHANGED', (newLocale: string) => { + if (i18nInstance) { + i18nInstance.setLocale(newLocale) + } + }) + return { template: '', - // Set locale from globals created() { - if (this.$i18n) { - this.$i18n.setLocale(locale) - } - }, - updated() { - if (this.$i18n) { + // Store i18n instance for LOCALE_CHANGED events + i18nInstance = this.$i18n + + // Set initial locale when component is created + if (locale && this.$i18n) { this.$i18n.setLocale(locale) } }, diff --git a/.storybook/theme.ts b/.storybook/theme.ts new file mode 100644 index 0000000000..3bb46e9677 --- /dev/null +++ b/.storybook/theme.ts @@ -0,0 +1,13 @@ +import { create } from 'storybook/theming/create' + +export const npmxDark = create({ + base: 'dark', + + brandTitle: 'npmx Storybook', + brandImage: '/npmx-storybook.svg', + + // UI + appContentBg: '#101010', // oklch(0.171 0 0) +}) + +export default npmxDark diff --git a/app/components/Button/Base.stories.ts b/app/components/Button/Base.stories.ts deleted file mode 100644 index 9de0744377..0000000000 --- a/app/components/Button/Base.stories.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { Meta, StoryObj } from '@storybook-vue/nuxt' -import Component from './Base.vue' - -const meta = { - component: Component, -} satisfies Meta - -export default meta -type Story = StoryObj - -export const Primary: Story = { - args: { - default: 'Primary Button', - }, -} - -export const Secondary: Story = { - args: { - default: 'Secondary Button', - variant: 'secondary', - }, -} - -export const Small: Story = { - args: { - default: 'Small Button', - size: 'small', - variant: 'secondary', - }, -} - -export const Disabled: Story = { - args: { - default: 'Disabled Button', - disabled: true, - }, -} - -export const WithIcon: Story = { - args: { - default: 'Search', - classicon: 'i-lucide:search', - variant: 'secondary', - }, -} - -export const WithKeyboardShortcut: Story = { - args: { - ariaKeyshortcuts: '/', - default: 'Search', - variant: 'secondary', - }, -} - -export const Block: Story = { - args: { - block: true, - default: 'Full Width Button', - }, -} diff --git a/app/components/Button/Base.vue b/app/components/Button/Base.vue index f37d57be06..88464d6986 100644 --- a/app/components/Button/Base.vue +++ b/app/components/Button/Base.vue @@ -1,6 +1,13 @@