I have created a Vue project with vue-cli
and installed storybook
later via npm install --save-dev storybook
as a dependency to show my created components.
The storybook webpack config looks like this:
const path = require('path')
module.exports = async ({ config, mode }) => {
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
sourceMap: false,
data: '@import "./src/assets/styles/main.scss";'
}
}
],
include: path.resolve(__dirname, '../')
}
)
return config
}
and the story index.js
like this:
import Vue from 'vue';
import { storiesOf } from '@storybook/vue';
import Alert from '../src/components/buttons/Alert.vue';
storiesOf('Components', module)
.add('Alert', () => ({
components: { Alert },
template: '<Alert />'
}))
for some reason when trying to load a component which consists of a SVG I'm getting:
The component itself is displayed but the part where the SVG sits no SVG is showing.
Interestingly though it works just fine when trying to display the component in the Vue's main app. The vue.config
looks like this:
module.exports = {
css: {
loaderOptions: {
sass: {
data: `@import "./src/assets/styles/main.scss";`
}
}
},
chainWebpack: config => {
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule.use("vue-svg-loader").loader("vue-svg-loader");
}
};
Why does storybook
does not load the svgs properly while the main Vue app can?
Edit: I have gone ahead and simply used webpacks file-loader
too to make sure it has nothing to do with vue-svg-loader
:
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {},
},
],
},
but I am getting the same error.
After trying to apply the first answer to replace push()
to rules with unshift()
I am getting this error: Error in parsing SVG: Non-whitespace before first tag
.
Edit2:
This is one of the SVGs I'm trying to load:
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="exclamation-triangle" class="svg-inline--fa fa-exclamation-triangle fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path></svg>
Edit3: *Alert.vue in projectroot/src/components/banners/Alert.vue
, Webpack config of Storybook is in projectroot/.storybook/webpack.config.js
<template>
<div v-if="show" :class="`alert_wrap ${model}`">
<IconBase>
<component :is="iconComponent" />
</IconBase>
<div class="alert_text">
<p v-if="title">{{ title }}</p>
<p>{{ msg }}</p>
</div>
<IconBase>
<TimesIcon @click="show = !show" aria-hidden="Close" />
</IconBase>
</div>
</template>
<script>
import IconBase from "../icons/IconBase.vue";
import CautionIcon from "../../assets/icons/exclamation-triangle-solid.svg";
import InfoIcon from "../../assets/icons/info-circle-solid.svg";
import SuccessIcon from "../../assets/icons/check-circle-solid.svg";
import TimesIcon from "../../assets/icons/times-solid.svg";
export default {
name: "Alert",
components: {
CautionIcon,
InfoIcon,
SuccessIcon,
TimesIcon,
IconBase
},
props: {
msg: {
type: String,
required: true
},
title: {
type: String
},
model: {
type: String,
default: "info",
validator(type) {
return ["caution", "info", "success"].includes(type);
}
}
},
computed: {
iconComponent() {
return `${this.model}-icon`;
}
},
data() {
return {
show: true
};
}
};
</script>
<style scoped lang="scss">
/* some styles depending on the props passed */
</style>
@liximomo's answer is almost correct. You should remove svg
file from file-loader
processing rule, but the rule search condition should be slightly different:
module.exports = ({ config }) => {
let rule = config.module.rules.find(r =>
// it can be another rule with file loader
// we should get only svg related
r.test && r.test.toString().includes('svg') &&
// file-loader might be resolved to js file path so "endsWith" is not reliable enough
r.loader && r.loader.includes('file-loader')
);
rule.test = /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/;
config.module.rules.push(
{
test: /\.svg$/,
use: ['vue-svg-loader']
}
)
// ...
return config;
}
Another way to import SVG without changing the Webpack config rules is to specify an inline loader, which can be useful to avoid errors when some components already use SVG in CSS or HTML as just a file path. Example:
// disable all rules with !! and use just vue-svg-loader
import CautionIcon from "!!vue-svg-loader!../../assets/icons/exclamation-triangle-solid.svg";