quasar
quasar copied to clipboard
Provide information how to use Quasar with Storybook
Is your feature request related to a problem? Please describe. For the past few weeks, I tried multiple times to make my Quasar project work with storybook, to no avail.
It works when no sass variables are used, but storybook fails to compile when it detects that sass variables are used anywhere, with an error about the variable being undefined.
I tried defining them and anything else i could think of, use various webpack sass loaders, import the variables from quasar, and other things that i no longer remember, none worked.
Describe the solution you'd like Any solution to how I can use Quasar with storybook properly.
Describe alternatives you've considered There are no alternatives to storybook that i'm aware of. Storybook is an important of the project's build process.
Additional context I created a reproduction repo of the error i'm getting, based on the Quasar 2 + storybook repo that someone built but fails with the same error when using sass variables.
Edit to be clear:
The error message is
SassError: Undefined variable.
Reproducttion: https://github.com/Seanitzel/Quasar-V2-Storybook
What is the error message you are facing?
Maybe this issue https://github.com/quasarframework/quasar/issues/11683?
The error message is
SassError: Undefined variable.
And I don't think it is related to the issue you tagged
Having the same issue, as a workaround, I re-imported it in my component using variables:
<style lang="scss">
// I have to add it for Storybook to avoid SassError: Undefined variable. background-color: rgba($primary-200, 0.8);
// TODO Configure Storybook properly
@import "src/css/quasar.variables.scss";
...
I'll let you know, if I find a good config.
This https://dev.to/yemolai/using-storybook-with-quasar-3090 looks inspiring (sharing webpack configuration between Storybook and Quasar) but it seems to remake what css configuration https://quasar.dev/quasar-cli/quasar-conf-js#property-css is meant for.
We must use css variables as explained here https://quasar.dev/style/color-palette#dynamic-change-of-brand-colors-dynamic-theme-colors-
@badsaarow replied here giving an example https://github.com/badsaarow/quasar2-storybook-boilerplate/issues/47#issuecomment-1057931751
@jclaveau what is the correct way to set the spacing variables? I'm struggling with how to transfer this file to storybook
I have Quasar + Vite + Storybook running with the following preview.ts:
import type { Preview } from '@storybook/vue3';
import '@quasar/extras/mdi-v7/mdi-v7.css';
import 'quasar/dist/quasar.css';
import { setup } from '@storybook/vue3';
import { Quasar } from 'quasar';
setup((app) => {
app.use(Quasar, {});
});
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;
This works, except my custom styles from app.sass and quasar.variables.sass won't get loaded.
A way to use Storybook 7 with Quasar / Vite and SCSS support
Use Vite 4
- [ ] Update Quasar to use Vite 4
npm remove @intlify/vite-plugin-vue-i18n # outdated
npm install @quasar/[email protected]
npm update @quasar/vite-plugin
npm update @vitejs/plugin-vue
- [ ] Ensure Vite version by using
yarn whyor https://github.com/amio/npm-why
Install Storybook
- [ ]
npx storybook@latest initas idicated here https://storybook.js.org/docs/react/get-started/install - [ ] Storybook lint rules seems to have incompatibilities with Quasar ones so I don't recommend them. Allowing them adds
plugin:storybook/recommendedto.elintrc.cjs
Extract the Vite config generated by Quasar (based on quasar.config.js) and inject it into Storybook's Vite
- [ ] in
.storybookcreate the filequasar-config-result.jswith the following content (Kept in JS as Quasar is written in JS)
import { QuasarConfFile } from '@quasar/app-vite/lib/quasar-config-file'
import { getQuasarCtx } from '@quasar/app-vite/lib/utils/get-quasar-ctx'
import { extensionRunner } from '@quasar/app-vite/lib/app-extension/extensions-runner'
// This code is taken from @quasar/app/bin/quasar-inspect
export async function getQuasarConfig (mode='spa', debug=true, cmd='dev', hostname=9000, port=9000) {
// Requires adding
// // https://github.com/evanw/esbuild/issues/1921#issuecomment-1197938703
// + "\nimport { createRequire } from 'module';const require = createRequire(import.meta.url);"
// to apps/quasar/node_modules/@quasar/app-vite/lib/quasar-config-file.js / createEsbuildConfig () / esbuilConfig.banner.js
const ctx = getQuasarCtx({
mode: mode,
target: mode === 'cordova' || mode === 'capacitor'
? 'android'
: void 0,
debug: debug,
dev: cmd === 'dev',
prod: cmd === 'build'
// vueDevtools?
})
await extensionRunner.registerExtensions(ctx)
const quasarConfFile = new QuasarConfFile({
ctx,
port: port,
host: hostname,
watch: undefined
})
const quasarConf = await quasarConfFile.read()
if (quasarConf.error !== void 0) {
throw new Error(quasarConf.error)
}
// console.log('quasarConf', quasarConf)
const modeConfig = (await import(`@quasar/app-vite/lib/modes/${mode}/${mode}-config.js`))?.modeConfig
const cfgEntries = []
let threadList = Object.keys(modeConfig)
for (const name of threadList) {
cfgEntries.push({
name,
object: await modeConfig[ name ](quasarConf)
})
}
return cfgEntries
}
- [ ] As mentioned in the previous script, we need to patch
/node_modules/@quasar/app-vite/lib/quasar-config-file.jsto support Esbuild to ESM so I added a patch in/patches/@quasar+app-vite+2.0.0-alpha.11.patchto use with https://github.com/ds300/patch-package like
diff --git a/node_modules/@quasar/app-vite/lib/quasar-config-file.js b/node_modules/@quasar/app-vite/lib/quasar-config-file.js
index 904363d..9645fa6 100644
--- a/node_modules/@quasar/app-vite/lib/quasar-config-file.js
+++ b/node_modules/@quasar/app-vite/lib/quasar-config-file.js
@@ -67,12 +67,28 @@ function createEsbuildConfig () {
},
banner: {
js: quasarConfigBanner
+ // https://github.com/evanw/esbuild/issues/1921#issuecomment-1197938703
+ + `
+ // This is probably due to the fact we use the alpha version of this package.
+ // TODO remove it once it is released
+ import { createRequire } from 'node:module';
+ const cjsRequire = createRequire(import.meta.url);
+ const require = (path) => {
+ const matches = path.match(/^(.+)\.mjs$/)
+
+ if (matches !== null) {
+ path = matches[1] + '.js'
+ }
+
+ return cjsRequire(path);
+ }
+ `
},
define: quasarEsbuildInjectReplacementsDefine,
resolveExtensions: [ appPaths.quasarConfigOutputFormat === 'esm' ? '.mjs' : '.cjs', '.js', '.mts', '.ts', '.json' ],
entryPoints: [ appPaths.quasarConfigFilename ],
outfile: tempFile,
- plugins: [ quasarEsbuildInjectReplacementsPlugin ]
+ plugins: [ quasarEsbuildInjectReplacementsPlugin ],
}
}
- [ ] In
/.storybook/main.tsinject the extracted config (This could probably be more complete but it works like that for now)
import type { StorybookConfig } from '@storybook/vue3-vite';
import type { Options } from '@storybook/types';
import type { InlineConfig, PluginOption } from 'vite';
import { mergeConfig } from 'vite'
import { getQuasarConfig } from './quasar-config-result';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/vue3-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
async viteFinal(config: InlineConfig, options: Options): Promise<Record<string, any>> {
// https://github.com/storybookjs/builder-vite#migration-from-webpack--cra
const quasarConfig = await getQuasarConfig()
const quasarViteConfig = quasarConfig[0].object
// console.log('quasarConfig', quasarViteConfig.server)
// console.log('storybook Vite config', JSON.stringify(config, null, 2))
// Quasar's Vite Plugins
const quasarVitePluginNames = quasarViteConfig.plugins.map((pluginConfig: PluginOption) => {
if (pluginConfig instanceof Promise) {
throw new Error('Promise is not supported for Quasar\'s vite config merge')
}
if (Array.isArray(pluginConfig)) {
// TODO are these config required for stories rendering?
console.warn('Arrays of Vite PluginOption are not supported for Quasar\'s vite config merge', JSON.stringify(pluginConfig, null, 2))
}
else if (pluginConfig !== false) {
return pluginConfig.name
}
})
// We must remove Vue plugins from Storybook before injecting Quasar's ones
config.plugins = config.plugins.filter((pluginConfig: PluginOption) => {
if (pluginConfig instanceof Promise) {
throw new Error('Promise is not supported for Quasar\'s vite config merge')
}
if (pluginConfig instanceof Array) {
throw new Error('Arrays of Vite PluginOption are not supported for Quasar\'s vite config merge')
}
return !pluginConfig || pluginConfig.name == null || ! quasarVitePluginNames.includes(pluginConfig.name)
})
config.plugins = [...config.plugins, ...quasarViteConfig.plugins]
const updatedConfig: Record<string, any> = mergeConfig(config, {
resolve: {
alias: {
...quasarViteConfig.resolve.alias
},
},
server: {
...quasarViteConfig.server
},
// Avoid error Failed to load url /sb-preview/runtime.js (resolved id: /sb-preview/runtime.js). Does the file exist?
// [vite]: Rollup failed to resolve import "/./sb-preview/runtime.js" from "/home/jean/dev/Hippocast/prototype/apps/quasar/iframe.html".
// This is most likely unintended because it can break your application at runtime.
// If you do want to externalize this module explicitly add it to
// `build.rollupOptions.external`
build: {
rollupOptions: {
external: [
/sb-preview\/runtime.js$/,
]
}
}
});
return updatedConfig
},
};
export default config;
Boot Storybook's Vue app like the Quasar one
- [ ] Create a
.storybook/client-entry-storybook.jscontaining
/**
* This file is a copy of .quasar/client-entry.js Vue app object injected.
* As client-entry.js is generated from template, you may have to patch it
* if you change quasar.conf.js
*/
// When quasar.config.js changes, css files must be manually imported here as in apps/quasar/.quasar/client-entry.js
import '@quasar/extras/roboto-font/roboto-font.css'
import '@quasar/extras/material-icons/material-icons.css'
// We load Quasar stylesheet file
import 'quasar/dist/quasar.sass'
import 'src/css/app.scss'
console.info('[Quasar] Running SPA for Storybook.')
const publicPath = ``
export async function start ({ app, router, store }, bootFiles) {
let hasRedirected = false
const getRedirectUrl = url => {
try { return router.resolve(url).href }
catch (err) {}
return Object(url) === url
? null
: url
}
const redirect = url => {
hasRedirected = true
if (typeof url === 'string' && /^https?:\/\//.test(url)) {
window.location.href = url
return
}
const href = getRedirectUrl(url)
// continue if we didn't fail to resolve the url
if (href !== null) {
window.location.href = href
// window.location.reload()
}
}
const urlPath = window.location.href.replace(window.location.origin, '')
for (let i = 0; hasRedirected === false && i < bootFiles.length; i++) {
try {
await bootFiles[i]({
app,
router,
store,
ssrContext: null,
redirect,
urlPath,
publicPath
})
}
catch (err) {
if (err && err.url) {
redirect(err.url)
return
}
console.error('[Quasar] boot error:', err)
return
}
}
if (hasRedirected === true) {
return
}
// App mounting is handled by Storybook
// app.mount('#q-app')
}
- [ ] Then change
.storybook/preview.tsto use Quasar upon Storybook's Vue instance
import type { Preview } from '@storybook/vue3';
import { setup } from '@storybook/vue3';
import type { App } from 'vue';
// Customize Storybook's style
import './storybook.scss'
import quasarUserOptions from '../.quasar/quasar-user-options' // lang / iconset
import { start } from './client-entry-storybook.js'
type ModuleType = {
default: null | Function
}
import createStore from '../src/stores/index'
import createRouter from '../src/router/index'
import { Quasar } from 'quasar'
import { markRaw } from 'vue'
(async () => {
// Chromatic doesn't support top level awaits
// Top-level await is not available in the configured target environment ("chrome87", "edge88", "es2020", "firefox78", "safari14" + 2 overrides)
// The store and router instanciation must be done here as Storybook's setup() fn doesn't support async
const store = typeof createStore === 'function'
? await createStore({})
: createStore
// Those are required by createRouter()
global.process = {
env: {
VUE_ROUTER_MODE: 'hash', // Use a routing through hash to avoid ovewriting Storybook url parts (e.g. http://192.168.1.8:6006/iframe.html?globals=backgrounds.grid:!true;backgrounds.value:!hex(F8F8F8)&args=#/)
VUE_ROUTER_BASE: '/iframe.html', // The url of Storybook's preview iframe
},
}
const router = markRaw(
typeof createRouter === 'function'
? await createRouter({store})
: createRouter
)
setup((app: App) => {
app.use(Quasar, quasarUserOptions)
app.use(store)
store.use(({ store }) => { store.router = router })
// router must be used before boot files to avoid "[Vue warn]: Failed to resolve component: router-view" in SB
// TODO ensure this works always. Does vueRouterMode: 'history' in config impact it?
app.use(router)
return Promise.all([
// When quasar.config.js changes, boot files must be manually imported here as in apps/quasar/.quasar/client-entry.js
// import('../src/boot/i18n'),
// import('boot/axios'),
])
.then((bootFiles: ModuleType[]) => {
const boot = bootFiles
.map((entry: ModuleType) => {
return entry.default
})
.filter(entry => entry instanceof Function)
start({app, router, store}, boot)
})
});
})()
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;
- [ ] As you can see I added a
.storybook/storybook.scssto change the error display of Storybook errors and for the story of the App component below
// This makes SB stories still visible in case of js exception
.sb-wrapper {
position: relative !important;
max-height: 300px;
overflow-y: auto;
}
// Avoids infinite width producing errors on Chromatic due to the 25 millions pixels threshold passed
.fixed, .fixed-full, .fullscreen, .fixed-center, .fixed-bottom, .fixed-left, .fixed-right, .fixed-top, .fixed-top-left, .fixed-top-right, .fixed-bottom-left, .fixed-bottom-right {
position: absolute !important;
}
.storybook-anti-oversize-wrapper {
max-width: 2000px;
overflow-y: auto;
border: 1px solid #888;
}
Add some sample stories
- [ ] Remove Storybook's examples
- [ ] Create a story for the Quasar EssentialLink example component
/src/stories/EssentialLink.stories.tscontaining
import type { Meta, StoryObj } from '@storybook/vue3';
import EssentialLink from '../components/EssentialLink.vue';
// More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction
const meta = {
title: 'Example/EssentialLink',
component: EssentialLink,
// This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs
tags: ['autodocs'],
argTypes: {
iconColor: { control: 'select', options: ['primary', 'secondary', 'accent'] },
// onClick: { action: 'clicked' },
},
args: { iconColor: 'accent' }, // default value
} satisfies Meta<typeof EssentialLink>;
export default meta;
type Story = StoryObj<typeof meta>;
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.js.org/docs/vue/api/csf
* to learn how to use render functions.
*/
export const Simple: Story = {
args: {
// iconColor: 'primary',
title: 'My essential link',
caption: 'caption',
link: 'https://www.google.com',
icon: 'warning',
},
};
export const LongText: Story = {
args: {
title: 'Aenean neque urna, aliquam in nunc ac, volutpat finibus lectus. Etiam quis orci ut est blandit vestibulum id ut mauris. Cras consequat erat in elit convallis tempor. Duis quis nibh accumsan nibh congue vestibulum sed et nisl. Aliquam imperdiet suscipit magna, a vulputate lorem facilisis vel. Donec facilisis vehicula suscipit. Donec scelerisque vel sapien et posuere. Nunc sit amet lacinia metus. Vivamus egestas nulla in lectus fermentum varius. ',
caption: 'caption',
link: 'https://www.google.com',
icon: 'warning',
},
};
- [ ] A modified a bit this component to control the icon color from the Story to check sass variables support
<template>
<q-item
clickable
tag="a"
target="_blank"
:href="link"
>
<q-item-section
v-if="icon"
avatar
>
<q-icon :name="icon" :color="iconColor"/>
</q-item-section>
<q-item-section>
<q-item-label>{{ title }}</q-item-label>
<q-item-label caption>{{ caption }}</q-item-label>
</q-item-section>
</q-item>
</template>
<script setup lang="ts">
export interface EssentialLinkProps {
title: string;
caption?: string;
link?: string;
icon?: string;
iconColor?: string;
}
withDefaults(defineProps<EssentialLinkProps>(), {
caption: '',
link: '#',
icon: '',
iconColor: '',
});
</script>
- [ ] Add a Story for the App component
src/stories/App.stories.ts. Theroutecontrol allows navigation
import type { Meta, StoryObj } from '@storybook/vue3';
import { useArgs } from '@storybook/preview-api';
import App from '../App.vue';
// This is definitelly not the best way to add custom args
// TODO investigate https://stackoverflow.com/a/72223811/2714285 and find a proper way to do this
type AppArgs = {
route: string;
};
type InputPropOverrides = {
args: AppArgs;
};
const meta = {
title: 'App',
component: App,
args: {
route: '/',
},
render: (args) => {
const [_, updateArgs] = useArgs();
window.location.hash = args.route
return {
components: { App },
data: () => {
return { args }
},
watch:{
$route (to) {
if (to.fullPath != args.route) {
updateArgs({ route: to.fullPath })
}
},
},
template: `
<div class="storybook-anti-oversize-wrapper">
<App />
</div>
`,
}
},
} satisfies Meta<typeof App> & InputPropOverrides;
export default meta;
type Story = StoryObj<typeof meta>;
export const AppSimple: Story & InputPropOverrides = {
args: {
route: '/',
},
};
Track .quasar changes to update Storybook config if needed
- [ ] Comment .quasar in
.gitignore
Configure Chromatic
- [ ] Create a
.github/workflows/chromatic.ymlfile containing (you'll need to set your working directory):
name: 'Chromatic Publish'
# https://github.com/chromaui/action
# https://www.chromatic.com/docs/github-actions
on: pull_request # Avoids passing Chromatic's snapshots threshold
# https://stackoverflow.com/a/72408109/2714285
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test:
# Disable chromatic push during Renovate's updates
if: ${{ github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]' }}
defaults:
run:
working-directory: apps/quasar
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Required to retrieve git history
# https://github.com/renovatebot/renovate/issues/7716#issuecomment-734391360
- name: Read Node.js version from .nvmrc
run: echo "NODE_VERSION=$(cat ../../.nvmrc)" >> $GITHUB_OUTPUT
id: nvm
- name: Dump Node.js version from .nvmrc
run: echo "NODE_VERSION=$(cat ../../.nvmrc)"
# https://github.com/actions/setup-node#caching-global-packages-data
- uses: actions/setup-node@v3
with:
# https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#multiple-operating-systems-and-architectures
node-version: ${{ steps.nvm.outputs.NODE_VERSION }}
- name: npm install
run: npm install
- uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
workingDir: apps/quasar
- [ ] Add the following line to your package.json
"scripts": {
// ...
"chromatic": "npx chromatic --exit-zero-on-changes",
"postinstall": "patch-package"
},
Conclusion
This way I'm able to use Storybook 7 with Quasar 2.12.1 and Typescript but
- IMHO a lot of this code belong to the Quasar team like
- Extracting Vite config / generating the viteFinal method
- Booting the Quasar app based on quasar.config.js (boot files and scss imports)
- Esbuild ESM imports
- Also, auto-configuring Storybook from Quasar CLI would be incredible
- Here I only cover Vite support (without being sure it's enough). One year ago I tried with Webpack, it worked but was real pain to achieve. But globally it's the same work.
Hoping supporting Storybook from the CLI (Webpack and Quasar) as a full featured component driven dev stack or Histoire for a lighter solution would interest the Quasar Team.
Hoping those inputs will help you!
Btw there are some related discussions
- https://github.com/quasarframework/quasar/discussions/13147
- https://github.com/quasarframework/quasar/discussions/15585
- https://github.com/quasarframework/quasar/discussions/11537
- The feature request I just opened: https://github.com/quasarframework/quasar/discussions/16037
@jclaveau where do you get import { getQuasarCtx } from '@quasar/app-vite/lib/utils/get-quasar-ctx'
I'm currently adding storybook to my project but its failing on that part:
Error: Cannot find module '@quasar/app-vite/lib/utils/get-quasar-ctx'
I was able to find that get-quasar-ctx file under @quasar/app-vite/lib/helper/get-quasar-ctx but its not a function.
@jclaveau where do you get
import { getQuasarCtx } from '@quasar/app-vite/lib/utils/get-quasar-ctx'I'm currently adding storybook to my project but its failing on that part:
Error: Cannot find module '@quasar/app-vite/lib/utils/get-quasar-ctx'I was able to find that get-quasar-ctx file under@quasar/app-vite/lib/helper/get-quasar-ctxbut its not a function.
I installed quasar as a dev dependency of my project instead of globally (using pnpm this produces no disk space issue).
Btw, just to warn you : Storybook 7 vite plugin has an issue as it calls the setup() hook and the Vue mount() function in parallel (at least a month ago it was like that). This produces some issues as the app is mounted before the Quasar boot files are fully run.
So if your components use boot files like i18n or, in my case, a boot file to set components default properties values, you will have some troubles.
I hope I will have some time to find a solution for it but for bow I'm blocked.
Thank you @jclaveau for all the efforts that you've put into this. I've recently stumbled upon this issue of providing scss variables that would override default quasar values. And it's stopping me from implementing sb integration for our project's component library. I'll be definitely trying out your solution in the upcoming days, otherwise I'll have to resort to implementing our own mini-version of sb. Thanks again!
To the quasar devs that might see this issue - I think it'd be really great for the otherwise awesome and complete quasar ecosystem to have an out-of-box storybook integration.
I'll be definitely trying out your solution in the upcoming days, otherwise I'll have to resort to implementing our own mini-version of sb.
Maybe Histoire would be easier ton integrate https://histoire.dev. If I remember well, there is an opened issue concerning Quasar support
@jclaveau I have been able to get it running for a few of my pages and the main two hurdles that are left have just felt impossible to resolve. Mainly if any page uses $q variable for example $q.notify will give you a error:
and any other $q features like $q.dark or $q.localstorage. I assume that is what you are meaning with the: 'Storybook 7 vite plugin has an issue as it calls the setup() hook and the Vue mount() function in parallel'
And the other main problem I ran into was the routing. I was able to resolve this mostly by adding:
async viteFinal(config) {
if (config.resolve) {
config.resolve.alias = {
...config.resolve.alias,
src: path.resolve(__dirname, '../src'),
};
}
return config;
},
In my main.ts storybook file, but this only works if I have full paths on all my pages, meaning an import like:
import LoginComponent from 'components/LoginComponent.vue';
On the LoginPage.vue gives a error in storybook but I know that import works. But if I just change it to:
import LoginComponent from 'src/components/LoginComponent.vue';
Then storybook stops complaining but thats not realistic for all cases since sometimes I will have dynamic paths.
Have you maybe found a way around these problems?
Mainly if any page uses $q variable for example $q.notify will give you a error: I assume that is what you are meaning with the: 'Storybook 7 vite plugin has an issue as it calls the setup() hook and the Vue mount() function in parallel'
It looks a lot like the issue due to unmounted boot files. This said, this change may have fixed in https://github.com/storybookjs/storybook/pull/23772/files#diff-761b3fb7b014b2341fea7300ea1308e11425ab0aeef90cbc5e0fa5a9acab0b5fR30
And the other main problem I ran into was the routing. I was able to resolve this mostly by adding:
async viteFinal(config) { if (config.resolve) { config.resolve.alias = { ...config.resolve.alias, src: path.resolve(__dirname, '../src'), }; } return config; },
Interesting, I personally avoid using aliases. I wonder if you speak about Vue Router but, if it's the case, to make it work I simply configured the hash mode instead of history in storybook (storybook passes other GET parameters to the view)
In my main.ts storybook file, but this only works if I have full paths on all my pages, meaning an import like:
import LoginComponent from 'components/LoginComponent.vue';On the LoginPage.vue gives a error in storybook but I know that import works. But if I just change it to:import LoginComponent from 'src/components/LoginComponent.vue';Then storybook stops complaining but thats not realistic for all cases since sometimes I will have dynamic paths.
I personally avoid dynamic paths and aliases as it makes me loose too much time with failing intellisense.
Have you maybe found a way around these problems? I personally staled this issue by stopping using Storybook due to tough deadlines but knowing that async
setup()functions are now supported (if this PR is published) I will probably dig again.
If I were you I would begin by updating Storybook to check if Quasar boot files work now as expected
I got Quasar with Storybook working. Here's detail.
I've read through a few sources:
- The comment in this thread https://github.com/quasarframework/quasar/issues/11654#issuecomment-1618295838
- This blog post https://javascript.plainenglish.io/setup-storybook-for-the-quasar-project-vite-pinia-vue-i18n-69b41745af60 and related repo https://github.com/tasynguyen3894/demo_quasar_storybook_i18n_pinia
- https://github.com/devhero/storybook-quasar-vite/
- https://github.com/quasarframework/quasar/discussions/16037
The JavaScript in Plain English (Medium) post got Quasar components to render but I was having trouble rendering my own because I ran into defineComponent not being available, which was caused by not having vite plugins added, heres my vite plugins example:
Example unplugin-auto-import/vite with storybook
CAUTION! This is a partial example, just pointing out how config.plugins.push worked for me.
import AutoImport from 'unplugin-auto-import/vite';
const config: StorybookConfig = {
async viteFinal(config) {
if (!config.plugins) {
config.plugins = [];
}
config.plugins.push(
AutoImport({
imports: [
'vue',
],
dts: 'src/auto-imports.d.ts',
}),
);
Thank you to all who are contributing to this feature! If I were starting fresh I would look at https://github.com/tasynguyen3894/demo_quasar_storybook_i18n_pinia/ and the related blog post and then make sure relevant vite plugins are loaded.
@josephdpurcell Did you by any chance follow up with the latest versions of quasar and app-vite?
Cloning your repo worked great..but trying to reproduce this with app-vite-2 has me going in circles.
In case this helps anyone else trying to set up Quasar + Storybook (using TS + Vite)
https://github.com/bigjump/quasar-storybook-demo
- First commit - I just ran
npm init quasar - Second commit - includes the changes required to get a simple demo working
You can see the changes here:
https://github.com/bigjump/quasar-storybook-demo/compare/e8ca641ca8e1665b415d89c08d1a26c820438846..56bf27d47fca198c93dd4e25943b2557eb40c59f#diff-d42715fd3297e575fb61faba39bba1b83739d3fd533a719ccfb0d81f64862b15
In summary:
Storybook and Quasar require different versions of Vite, so we need to manage it seperately. Steps are:
- Create
.storybookfolder withpreview.tsandmain.ts- see Storybook docs about these - Add storybook commands to
package.jsonscripts - Add storybook dev dependencies into
package.json - Override the Vite & ViteJs plugin in
package.json - Add
vite.config.js - Create your first storybook story - eg.
DemoCard.stories.tsassuming you have aDemoCard.vuecomponent npm run storybook