Vuetify 3: Use Svg as a v-icon - vue.js

I would like to use my custom svg as a v-icon but I don't find any solutions in the Vuetify v3 documentation.
In the vuetify v2, I can do this kind of things in my vuetify.js:
export default new Vuetify({
icons:{
values: {
test: {
component: Test,
},
And I can use this like this:
<v-icon size="40">$vuetify.icons.test</v-icon>
How I can do the same thing in Vuetify v3 ? Thanks for your help :)

Below code shows an example of adding a custom icon along with the mdi set of icons to Vuetify and using both in a component via aliases.
vuetify.js
import { createVuetify } from 'vuetify'
import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
import folder from '#/customIcons/folderIcon.vue'
const aliasesCustom = {
...aliases,
folder,
}
export const vuetify = createVuetify({
icons: {
defaultSet: 'mdi',
aliases: {
...aliasesCustom
},
sets: {
mdi,
},
},
})
folderIcon.vue (your custom icon)
<template>
<svg>...</svg>
</template>
any SFC
<template>
<v-icon>$folder</v-icon>
<v-icon>$mdiGithub</v-icon>
</template>
Original source: this thread in the Vuetify discord channel

Related

How can I wrap a vue component during cypress component testing?

I am using component testing in Cypress on Vue. My project components use the vuetify plugin.
Currently, tested components load with Vuetify:
import DebuggingTemporaryComponent from "./DebuggingTemporaryComponent";
import {mount} from "#cypress/vue";
import vuetify from '../../resources/js/vuetify'
it('mounts the component with vuetify', () => {
mount(DebuggingTemporaryComponent,{vuetify,})
cy.contains('Hello World') ✅
}
However, the stylings are not functioning correctly because Vuetify components need to be wrapped in a <v-app> at least once on the page. In component testing this is not happening.
I need to customise the wrapper as suggested here in the docs for the React equivalent. However whenever I try to make my own function to do this, I get an error that the appropriate webpack loader isn't there. Vue loader is there and working.
import {mount as cypressMount} from '#cypress/vue'
export function mount (component){
return cypressMount(<v-app>component</v-app>, prepareComponent(props, options))
}
Can anyone help me with where to go next with this?
You can construct a simple wrapper in the test, for example
Component to test - Button.vue
<template>
<v-btn color="red lighten-2" dark>
Click Me
</v-btn>
</template>
Test
import Button from "./Button";
import {mount} from "#cypress/vue";
import vuetify from '../plugins/vuetify'
import { VApp } from 'vuetify/lib/components'
const WrapperComp = {
template: `
<v-app>
<Button />
</v-app>
`,
components: {
VApp,
Button
}
}
it('mounts the component with vuetify', () => {
mount(WrapperComp, { vuetify })
const lightRed = 'rgb(229, 115, 115)'
cy.contains('button', 'Click Me') // ✅
.should('have.css', 'background-color', lightRed) // fails if no <v-app> used above
})
You get an error because you are trying to use JSX which is indeed possible with Vue but you need to configure additional build plugins.
Same can be achieved without JSX by using render function
import {mount} from "#cypress/vue";
import vuetify from '../../resources/js/vuetify'
import { VApp } from 'vuetify/lib/components'
function mountComponentWithVuetify(componentToMount, options = {})
{
return mount({
render(h) {
return h(VApp, [h(componentToMount)])
}
},
{
vuetify,
...options,
})
}

V-Checkbox icon missing with vuetify & #mdi/js. Whats the best way to import it?

I want to know how to properly use vuetify components that itself use icons with #mdi/js.
My vuetify config looks like this:
vuetify: {
iconfont: 'mdiSvg',
defaultAssets: false,
...
}
Just importing the icons and overriding them in the v-checkbox works, but I don't want to do that with every v-checkbox I use:
<template>
<v-checkbox
v-model="checkboxModel"
:indeterminate-icon="mdiCheckboxMarkedOutline"
:on-icon="mdiCheckboxMarked"
:off-icon="mdiCheckboxBlankOutline"
:label="Checkbox"
></v-checkbox>
</template>
<script>
import {
mdiCheckboxBlankOutline,
mdiCheckboxMarked,
mdiCheckboxMarkedOutline,
} from '#mdi/js'
export default {
data() {
return {
checkboxModel: false,
mdiCheckboxBlankOutline,
mdiCheckboxMarked,
mdiCheckboxMarkedOutline,
}
},
}
</script>
The other way would be to overide the $checkboxOff SASS variable.
In node_modules/vuetify/dist/vuetify.js I found the declatation of the variable.
checkboxOff: 'M19,3H5C3.89,3 3,3.89 3,5V19C3,20.1 3.9,21 5,21H19C20.1,21 21,20.1 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z',
This is the actual svg path of the icon (just paste it in there https://yqnn.github.io/svg-path-editor/ to see it). So I tried to override it in 'variable.scss' but with no success.
What am I missing?
https://vuetifyjs.com/en/features/icon-fonts/#using-custom-icons
If you want to specify your own icons that should be used all the time, you have to configure the 'values' option of your vuetify config during installation.
import {
mdiCheckboxBlankOutline,
mdiCheckboxMarked,
mdiCheckboxMarkedOutline,
} from '#mdi/js'
// vuetify options:
{
iconfont: 'mdiSvg',
defaultAssets: false,
values: {
checkboxOn: mdiCheckboxMarked,
checkboxOff: mdiCheckboxBlankOutline,
checkboxIndeterminate: mdiCheckboxMarkedOutline,
}
}
Actually the problam was my wrong config. Thats how it has to be
vuetify: {
icons: {
iconfont: 'mdiSvg',
}
...
}

How to import custom svg icon in Vuex (Vuetify)

How to import custom svg icon in Vuex (using Vuetify and Nuxt.js)
in Vuetify.options:
icons: {
values: {
myIcon: {
component: awesomeIcon
}
}
}
According to the docs here You would import the icon in the config file (for vuetify) and add it to the vuetify bootstrap as you did. Although you would need to have a default for all icons as shown in the docs above.
Basically should look something like this:
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import myImportedIcon from 'someplace'
Vue.use(Vuetify)
export default new Vuetify({
icons: {
iconfont: 'fa',
values: {
myIcon: myImportedIcon
},
},
})

How to get vuetify to display 'mdiSvg' icons?

Following the documentation here:
https://next.vuetifyjs.com/en/customization/icons
I setup my project exactly as described.
Expected:
seeing an <svg> tag.
Actual:
I see an <i> tag (which causes issues with some settings on IE)
Here is a link to a reproduction:
https://codesandbox.io/embed/dialog-vuetify-bhs76
This is the way I've been doing it. If you want, you can also set custom names for icons - which is very useful if you decide to change which icons you use:
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
import {
mdiDelete
} from '#mdi/js';
Vue.use(Vuetify);
export default new Vuetify({
icons: {
iconfont: 'mdiSvg',
values: {
mdiDelete,
// or give it a custom name:
delete: mdiDelete,
}
},
});
Then in your v-icon component:
<v-icon>$mdiDelete</v-icon>
<v-icon>$delete</v-icon>
you should install the right icon library from the vuetify site
then import it on the vuetify plugin folder
export default new Vuetify({
icons: {
iconfont: 'mdiSvg', // 'mdi' || 'mdiSvg' || 'md' || 'fa' || 'fa4' || 'faSvg'
},
})
least and last you should use the icon on the component
<template>
<div>
<v-icon>{{mdiMenu}}</v-icon>
</div>
</template>
<script>
import { mdiMenu } from '#mdi/js';
export default {
data() {
return {
mdiMenu
};
}
};
</script>
you can find icons on this material design site

Vuetify theme settings not working in Storybook

(Vue version - 2, Vuetify and Storybook - latest)
Consider the following simple button component:
<template>
<v-btn round
color="primary">
<slot></slot>
</v-btn>
</template>
<script>
export default {
name: "test-button",
}
</script>
In the App component file, it is invoked like this:
<v-app>
<v-layout column justify-center>
<v-layout row justify-center align-center>
<test-button #click="testBtnClicked">
Click It
</test-button>
</v-layout>
</v-layout>
</v-app>
And the Vuetify setup looks like this in the main.js:
import Vue from 'vue';
import 'vuetify/dist/vuetify.min.css';
import Vuetify from "vuetify";
import colors from 'vuetify/es5/util/colors';
Vue.use(Vuetify, {
theme: {
primary: colors.indigo.base,
info: colors.blue.lighten2,
accent: colors.green.lighten1,
error: colors.red.darken2
}
});
So far, so good - I get a nice indigo button in the middle of the frame, which is what I would expect.
Now, in the Storybook config, I'm using the same lines as in main.js to set up Vuetify, and my Storybook file looks like this:
import { storiesOf } from "#storybook/vue";
import TestButton from "./TestButton.vue";
storiesOf("TestButton", module)
.add("button template", () => ({
template: '<test-button :rounded="true">round</test-button>',
components: {TestButton}
}))
This renders the button in Storybook, but the theme settings don't happen, to wit: the button is not indigo, it is white. The rest of the Vuetify attributes seem to be fine - it looks like a Vuetify rounded button, with white text.
I'm not sure if this is a Vuetify issue or a Vue issue or a Storybook issue (or a user-error issue on my part). If anyone has done this, I'd appreciate some insight.
Here's my webpack.config.js (in .storybook):
module.exports = (baseConfig, env, defaultConfig) => {
defaultConfig.plugins.push(new VueLoaderPlugin());
return defaultConfig;
};
I have the problem too.
After reading vuetify's code, seems the generation of CSS, and injection of the theme is made in the VApp mixin app-theme located here.
So, I think the problem is not linked to storybook, but vuetify instead.
By wrapping the component I wish to test with a v-app, it's ok.
So, for now, please try to add a decorator in your config.js like this :
import { configure, addDecorator } from '#storybook/vue';
import 'vuetify/dist/vuetify.css';
import Vue from 'vue';
import Vuetify from 'vuetify';
Vue.use(Vuetify, {
theme: {
// your colors
},
});
addDecorator(() => ({
template: '<v-app><story/></v-app>',
}));
...
Sounds ok for you ?
(answer too on github : https://github.com/storybooks/storybook/issues/4256#issuecomment-440065778)
I ran into the issue a few months ago so its the not freshest in my mind. You need to import the plugin that adds Vuetify to Vue. Here is the config.js file in my .storybook folder.
// #### /.storybook/config.js
import { configure } from '#storybook/vue';
import Vue from 'vue';
import Vuex from 'vuex'; // Vue plugins
import '#/plugins/allPlugins';
// Install Vue plugins.
Vue.use(Vuex);
const req = require.context('../src', true, /\.stories\.js$/)
function loadStories() {
req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);
plugins/allPlugins
import Vue from 'vue'; // <---- important
import './vuetify'; // <---- important
import WebCam from 'vue-web-cam';
import Chat from '#/libs/vue-beautiful-chat/index';
import './styles';
import './ellipsis';
import 'viewerjs/dist/viewer.css';
import Viewer from 'v-viewer';
Vue.use(WebCam);
Vue.use(Chat);
Vue.use(Viewer);
plugins/vuetify
import Vue from 'vue';
import {
Vuetify,
VApp,
...
} from 'vuetify';
import colors from 'vuetify/es5/util/colors';
Vue.use(Vuetify, {
components: {
VApp,
...
},
theme: {
primary: colors.blue.lighten1,
...
},
});
The reason I separated out all plugins was because of custom styles and other plugins that I was more control of when importing. If you need custom styles you will need to import both Vuetify plugin and custom styles.