From c1f5529ce6958986ab9dbd0d73afea509def661e Mon Sep 17 00:00:00 2001 From: cylewaitforit Date: Tue, 17 Mar 2026 13:58:21 -0500 Subject: [PATCH] docs(ui): use storybook-i18n addon for locale switching --- .storybook/main.ts | 7 +++- .storybook/preview.ts | 51 +++++++++++++++-------------- app/components/Link/Link.stories.ts | 23 ++++++++++--- package.json | 1 + pnpm-lock.yaml | 19 +++++++++++ pnpm-workspace.yaml | 1 + 6 files changed, 72 insertions(+), 30 deletions(-) diff --git a/.storybook/main.ts b/.storybook/main.ts index 04f806021c..79aba40d35 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: { diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 3242f7788a..960cf90c97 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,5 +1,6 @@ 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' @@ -17,6 +18,14 @@ globalThis['__NUXT_COLOR_MODE__'] ??= { // @ts-expect-error - dynamic global name globalThis.defineOgImageComponent = fn() +// Subscribe to locale changes from storybook-i18n addon (once, outside decorator) +let currentI18nInstance: any = null +addons.getChannel().on('LOCALE_CHANGED', (newLocale: string) => { + if (currentI18nInstance) { + currentI18nInstance.setLocale(newLocale) + } +}) + const preview: Preview = { parameters: { controls: { @@ -26,24 +35,18 @@ const preview: Preview = { }, }, }, + 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 +73,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 @@ -84,14 +87,12 @@ const preview: Preview = { 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 + currentI18nInstance = this.$i18n + + // Set initial locale when component is created + if (locale && this.$i18n) { this.$i18n.setLocale(locale) } }, diff --git a/app/components/Link/Link.stories.ts b/app/components/Link/Link.stories.ts index 9ceeaeb7eb..8bf47715b2 100644 --- a/app/components/Link/Link.stories.ts +++ b/app/components/Link/Link.stories.ts @@ -5,14 +5,17 @@ const meta = { component: LinkBase, args: { to: '/', - default: 'Click me', }, } satisfies Meta export default meta type Story = StoryObj -export const Default: Story = {} +export const Default: Story = { + args: { + default: 'Click me', + }, +} export const ExternalLink: Story = { args: { @@ -75,16 +78,28 @@ export const WithIconButton: Story = { args: { variant: 'button-primary', classicon: 'i-lucide:copy', - default: 'Copy', }, + render: args => ({ + components: { LinkBase }, + setup() { + return { args } + }, + template: `{{ $t("package.readme.copy_as_markdown") }}`, + }), } export const WithKeyboardShortcut: Story = { args: { variant: 'button-secondary', ariaKeyshortcuts: 's', - default: 'Search', }, + render: args => ({ + components: { LinkBase }, + setup() { + return { args } + }, + template: `{{ $t("search.button") }}`, + }), } export const BlockLink: Story = { diff --git a/package.json b/package.json index ae1fd762d5..97b2e316ae 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "markdown-it-anchor": "9.2.0", "schema-dts": "1.1.5", "storybook": "catalog:storybook", + "storybook-i18n": "catalog:storybook", "typescript": "5.9.3", "unplugin-vue-markdown": "30.0.0", "vitest": "npm:@voidzero-dev/vite-plus-test@0.1.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0432500983..aa062e1ea3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,9 @@ catalogs: '@storybook/addon-themes': specifier: ^10.2.7 version: 10.3.0 + storybook-i18n: + specifier: ^10.1.1 + version: 10.1.1 overrides: sharp: 0.34.5 @@ -313,6 +316,9 @@ importers: storybook: specifier: ^10.2.7 version: 10.2.13(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook-i18n: + specifier: catalog:storybook + version: 10.1.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.13(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) typescript: specifier: 5.9.3 version: 5.9.3 @@ -9617,6 +9623,11 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + storybook-i18n@10.1.1: + resolution: {integrity: sha512-A2cFaGCysU2VkjDQUFY0aQsppAFIhZU3Tx38/NYLhWShTRkUpE3huGHP59CAgArxLQEzEjLmieiTSuNT2bwA4Q==} + peerDependencies: + storybook: ^10.2.7 + storybook@10.2.13: resolution: {integrity: sha512-heMfJjOfbHvL+wlCAwFZlSxcakyJ5yQDam6e9k2RRArB1veJhRnsjO6lO1hOXjJYrqxfHA/ldIugbBVlCDqfvQ==} hasBin: true @@ -22193,6 +22204,14 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + storybook-i18n@10.1.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.13(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)): + dependencies: + '@storybook/icons': 2.0.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + storybook: 10.2.13(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + transitivePeerDependencies: + - react + - react-dom + storybook@10.2.13(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@storybook/global': 5.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3713efa610..af19f679b8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -47,3 +47,4 @@ catalogs: '@storybook/addon-docs': '^10.2.7' '@storybook/addon-themes': '^10.2.7' 'storybook': '^10.2.7' + 'storybook-i18n': '^10.1.1'