Skip to content

Commit 306077d

Browse files
Merge branch 'main' into fix/blog-ui-and-comment-redesign
2 parents 8ecca37 + 1cd901f commit 306077d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1626
-212
lines changed

.storybook/main.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import type { StorybookConfig } from '@storybook-vue/nuxt'
22

33
const config = {
4-
stories: ['../app/**/*.stories.@(js|ts)'],
5-
addons: ['@storybook/addon-a11y', '@storybook/addon-docs', '@storybook/addon-themes'],
4+
stories: ['../.storybook/*.mdx', '../app/**/*.stories.@(js|ts)'],
5+
addons: [
6+
'@storybook/addon-a11y',
7+
'@storybook/addon-docs',
8+
'@storybook/addon-themes',
9+
'storybook-i18n',
10+
],
611
framework: '@storybook-vue/nuxt',
712
staticDirs: ['./.public'],
813
features: {
@@ -11,6 +16,33 @@ const config = {
1116
async viteFinal(newConfig) {
1217
newConfig.plugins ??= []
1318

19+
// Bridge compatibility between Storybook v10 core and v9 @storybook-vue/nuxt
20+
// v10 expects module federation globals that v9 doesn't provide
21+
newConfig.plugins.push({
22+
name: 'storybook-v10-compat',
23+
transformIndexHtml: {
24+
order: 'pre',
25+
handler(html) {
26+
const script = `
27+
<script>
28+
// Minimal shims for Storybook v10 module federation system
29+
// These will be replaced when Storybook runtime loads
30+
window.__STORYBOOK_MODULE_GLOBAL__ = { global: window };
31+
window.__STORYBOOK_MODULE_CLIENT_LOGGER__ = {
32+
deprecate: console.warn.bind(console, '[deprecated]'),
33+
once: console.log.bind(console),
34+
logger: console
35+
};
36+
window.__STORYBOOK_MODULE_CHANNELS__ = {
37+
Channel: class { on() {} off() {} emit() {} once() {} },
38+
createBrowserChannel: () => new window.__STORYBOOK_MODULE_CHANNELS__.Channel()
39+
};
40+
</script>`
41+
return html.replace(/<script>/, script + '<script>')
42+
},
43+
},
44+
})
45+
1446
newConfig.plugins.push({
1547
name: 'ignore-internals',
1648
transform(_, id) {

.storybook/manager.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import { addons } from 'storybook/manager-api'
2-
import { create } from 'storybook/theming'
32

4-
const npmxTheme = create({
5-
brandTitle: 'npmx Storybook',
6-
brandImage: '/npmx-storybook.svg',
7-
})
3+
import npmxDark from './theme'
84

95
addons.setConfig({
10-
theme: npmxTheme,
6+
theme: npmxDark,
7+
layoutCustomisations: {
8+
showToolbar: (state, defaultValue) => {
9+
if (state.viewMode === 'docs' && state.storyId) {
10+
const story = state.index?.[state.storyId]
11+
const tags = story?.tags || []
12+
if (tags.includes('hide-toolbar')) {
13+
return false
14+
}
15+
}
16+
return defaultValue
17+
},
18+
},
1119
})

.storybook/preview-head.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<style>
2+
/* Override docs story canvas background to match npmx theme */
3+
.docs-story {
4+
background-color: var(--bg, oklch(0.171 0 0)) !important;
5+
}
6+
</style>

.storybook/preview.ts

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import type { Preview } from '@storybook-vue/nuxt'
22
import { withThemeByDataAttribute } from '@storybook/addon-themes'
3+
import { addons } from 'storybook/preview-api'
34
import { currentLocales } from '../config/i18n'
45
import { fn } from 'storybook/test'
56
import { ACCENT_COLORS } from '../shared/utils/constants'
67

8+
import npmxDark from './theme'
9+
710
// related: https://github.com/npmx-dev/npmx.dev/blob/1431d24be555bca5e1ae6264434d49ca15173c43/test/nuxt/setup.ts#L12-L26
811
// Stub Nuxt specific globals
912
// @ts-expect-error - dynamic global name
@@ -17,6 +20,12 @@ globalThis['__NUXT_COLOR_MODE__'] ??= {
1720
// @ts-expect-error - dynamic global name
1821
globalThis.defineOgImageComponent = fn()
1922

23+
// Subscribe to locale changes from storybook-i18n addon (once, outside decorator)
24+
let currentI18nInstance: any = null
25+
addons.getChannel().on('LOCALE_CHANGED', (newLocale: string) => {
26+
currentI18nInstance?.setLocale(newLocale)
27+
})
28+
2029
const preview: Preview = {
2130
parameters: {
2231
controls: {
@@ -25,25 +34,22 @@ const preview: Preview = {
2534
date: /Date$/i,
2635
},
2736
},
37+
docs: {
38+
theme: npmxDark,
39+
},
40+
},
41+
initialGlobals: {
42+
locale: 'en-US',
43+
locales: currentLocales.reduce(
44+
(acc, locale) => {
45+
acc[locale.code] = locale.name
46+
return acc
47+
},
48+
{} as Record<string, string>,
49+
),
2850
},
2951
// Provides toolbars to switch things like theming and language
3052
globalTypes: {
31-
locale: {
32-
name: 'Locale',
33-
description: 'UI language',
34-
defaultValue: 'en-US',
35-
toolbar: {
36-
icon: 'globe',
37-
dynamicTitle: true,
38-
items: [
39-
// English is at the top so it's easier to reset to it
40-
{ value: 'en-US', title: 'English (US)' },
41-
...currentLocales
42-
.filter(locale => locale.code !== 'en-US')
43-
.map(locale => ({ value: locale.code, title: locale.name })),
44-
],
45-
},
46-
},
4753
accentColor: {
4854
name: 'Accent Color',
4955
description: 'Accent color',
@@ -70,9 +76,9 @@ const preview: Preview = {
7076
attributeName: 'data-theme',
7177
}),
7278
(story, context) => {
73-
const { locale, accentColor } = context.globals as {
74-
locale: string
79+
const { accentColor, locale } = context.globals as {
7580
accentColor?: string
81+
locale?: string
7682
}
7783

7884
// Set accent color from globals
@@ -84,14 +90,12 @@ const preview: Preview = {
8490

8591
return {
8692
template: '<story />',
87-
// Set locale from globals
8893
created() {
89-
if (this.$i18n) {
90-
this.$i18n.setLocale(locale)
91-
}
92-
},
93-
updated() {
94-
if (this.$i18n) {
94+
// Store i18n instance for LOCALE_CHANGED events
95+
currentI18nInstance = this.$i18n
96+
97+
// Set initial locale when component is created
98+
if (locale && this.$i18n) {
9599
this.$i18n.setLocale(locale)
96100
}
97101
},

.storybook/storybook-welcome.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Meta } from '@storybook/addon-docs/blocks';
2+
3+
<Meta title="Welcome" tags={['hide-toolbar']}/>
4+
5+
# Welcome
6+
7+
Welcome to the npmx Storybook.

.storybook/theme.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { create } from 'storybook/theming'
2+
3+
const npmxDark = create({
4+
base: 'dark',
5+
6+
brandTitle: 'npmx Storybook',
7+
brandImage: '/npmx-storybook.svg',
8+
9+
// UI
10+
appContentBg: '#101010', // oklch(0.171 0 0)
11+
})
12+
13+
export default npmxDark

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"editor.formatOnSave": true,
44
"i18n-ally.keystyle": "nested",
55
"i18n-ally.localesPaths": ["./i18n/locales"],
6-
"typescript.tsdk": "node_modules/typescript/lib",
6+
"js/ts.tsdk.path": "node_modules/typescript/lib",
77
"explorer.fileNesting.enabled": true,
88
"explorer.fileNesting.patterns": {
99
"*.vue": "${capture}.stories.ts"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<script setup lang="ts">
2+
/**
3+
* This component is designed to create various patterns from seeds, and included
4+
* inside vue-data-ui charts in the #pattern slots.
5+
* Using patterns helps users with vision deficency (like achromatopsia) to distinguish
6+
* series in the context of data visualisation.
7+
*/
8+
import { computed } from 'vue'
9+
import { createSeededSvgPattern, type ChartPatternSlotProps } from '~/utils/charts'
10+
11+
const props = defineProps<ChartPatternSlotProps>()
12+
13+
const pattern = computed(() =>
14+
createSeededSvgPattern(props.seed, {
15+
foregroundColor: props.foregroundColor,
16+
backgroundColor: props.color ?? props.fallbackColor,
17+
minimumSize: props.minSize,
18+
maximumSize: props.maxSize,
19+
}),
20+
)
21+
</script>
22+
23+
<template>
24+
<pattern
25+
:id
26+
patternUnits="userSpaceOnUse"
27+
:width="pattern.width"
28+
:height="pattern.height"
29+
:patternTransform="`rotate(${pattern.rotation})`"
30+
v-html="pattern.contentMarkup"
31+
/>
32+
</template>

app/components/Compare/FacetBarChart.vue

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { ref, computed } from 'vue'
33
import { VueUiHorizontalBar } from 'vue-data-ui/vue-ui-horizontal-bar'
44
import type { VueUiHorizontalBarConfig, VueUiHorizontalBarDatasetItem } from 'vue-data-ui'
55
import { getFrameworkColor, isListedFramework } from '~/utils/frameworks'
6+
import { createChartPatternSlotMarkup } from '~/utils/charts'
67
import { drawSmallNpmxLogoAndTaglineWatermark } from '~/composables/useChartWatermark'
8+
79
import {
810
loadFile,
911
insertLineBreaks,
@@ -213,13 +215,40 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
213215
backdropFilter: false,
214216
backgroundColor: 'transparent',
215217
customFormat: ({ datapoint }) => {
216-
const name = datapoint?.name?.replace(/\n/g, '<br>')
218+
const name = datapoint?.name?.replace(/\n/g, '<br>') ?? ''
219+
const safeSeriesIndex = (datapoint?.absoluteIndex as number) ?? 0
220+
const patternId = `tooltip-pattern-${safeSeriesIndex}`
221+
const usePattern = safeSeriesIndex !== 0
222+
223+
const patternMarkup = usePattern
224+
? createChartPatternSlotMarkup({
225+
id: patternId,
226+
seed: safeSeriesIndex,
227+
foregroundColor: colors.value.bg!,
228+
fallbackColor: 'transparent',
229+
maxSize: 24,
230+
minSize: 16,
231+
})
232+
: ''
233+
234+
const markerMarkup = usePattern
235+
? `
236+
<rect x="0" y="0" width="20" height="20" rx="3" fill="${datapoint?.color ?? 'transparent'}" />
237+
<rect x="0" y="0" width="20" height="20" rx="3" fill="url(#${patternId})" />
238+
`
239+
: `
240+
<rect x="0" y="0" width="20" height="20" rx="3" fill="${datapoint?.color ?? 'transparent'}" />
241+
`
242+
217243
return `
218244
<div class="font-mono p-3 border border-border rounded-md bg-[var(--bg)]/10 backdrop-blur-md">
219245
<div class="grid grid-cols-[12px_minmax(0,1fr)_max-content] items-center gap-x-3">
220246
<div class="w-3 h-3">
221-
<svg viewBox="0 0 2 2" class="w-full h-full">
222-
<rect x="0" y="0" width="2" height="2" rx="0.3" fill="${datapoint?.color}" />
247+
<svg viewBox="0 0 20 20" class="w-full h-full" aria-hidden="true">
248+
<defs>
249+
${patternMarkup}
250+
</defs>
251+
${markerMarkup}
223252
</svg>
224253
</div>
225254
<span class="text-3xs uppercase tracking-wide text-[var(--fg)]/70 truncate">
@@ -230,7 +259,7 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
230259
</span>
231260
</div>
232261
</div>
233-
`
262+
`
234263
},
235264
},
236265
},
@@ -243,8 +272,19 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
243272
<div class="font-mono facet-bar">
244273
<ClientOnly v-if="dataset.length">
245274
<VueUiHorizontalBar :key="chartKey" :dataset :config class="[direction:ltr]">
275+
<template #pattern="{ patternId, seriesIndex }">
276+
<ChartPatternSlot
277+
v-if="seriesIndex != 0"
278+
:id="patternId"
279+
:seed="seriesIndex"
280+
:foreground-color="colors.bg!"
281+
fallback-color="transparent"
282+
:max-size="24"
283+
:min-size="16"
284+
/>
285+
</template>
286+
246287
<template #svg="{ svg }">
247-
<!-- Inject npmx logo & tagline during SVG and PNG print -->
248288
<g
249289
v-if="svg.isPrintingSvg || svg.isPrintingImg"
250290
v-html="
@@ -261,15 +301,19 @@ const config = computed<VueUiHorizontalBarConfig>(() => {
261301
<span v-if="isOpen" class="i-lucide:x w-6 h-6" aria-hidden="true" />
262302
<span v-else class="i-lucide:ellipsis-vertical w-6 h-6" aria-hidden="true" />
263303
</template>
304+
264305
<template #optionCsv>
265306
<span class="text-fg-subtle font-mono pointer-events-none">CSV</span>
266307
</template>
308+
267309
<template #optionImg>
268310
<span class="text-fg-subtle font-mono pointer-events-none">PNG</span>
269311
</template>
312+
270313
<template #optionSvg>
271314
<span class="text-fg-subtle font-mono pointer-events-none">SVG</span>
272315
</template>
316+
273317
<template #optionAltCopy>
274318
<span
275319
class="w-6 h-6"

app/components/Link/Link.stories.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ const meta = {
55
component: LinkBase,
66
args: {
77
to: '/',
8-
default: 'Click me',
98
},
109
} satisfies Meta<typeof LinkBase>
1110

1211
export default meta
1312
type Story = StoryObj<typeof meta>
1413

15-
export const Default: Story = {}
14+
export const Default: Story = {
15+
args: {
16+
default: 'Click me',
17+
},
18+
}
1619

1720
export const ExternalLink: Story = {
1821
args: {
@@ -75,16 +78,28 @@ export const WithIconButton: Story = {
7578
args: {
7679
variant: 'button-primary',
7780
classicon: 'i-lucide:copy',
78-
default: 'Copy',
7981
},
82+
render: args => ({
83+
components: { LinkBase },
84+
setup() {
85+
return { args }
86+
},
87+
template: `<LinkBase v-bind="args">{{ $t("package.readme.copy_as_markdown") }}</LinkBase>`,
88+
}),
8089
}
8190

8291
export const WithKeyboardShortcut: Story = {
8392
args: {
8493
variant: 'button-secondary',
8594
ariaKeyshortcuts: 's',
86-
default: 'Search',
8795
},
96+
render: args => ({
97+
components: { LinkBase },
98+
setup() {
99+
return { args }
100+
},
101+
template: `<LinkBase v-bind="args">{{ $t("search.button") }}</LinkBase>`,
102+
}),
88103
}
89104

90105
export const BlockLink: Story = {

0 commit comments

Comments
 (0)