How to mock modules in storybook's stories? - vue.js

I have a Vue component which includes some external modules with complicated logic. For example:
// Component.vue
import Vue from 'vue';
import { externalModule } from '#/path/to/module';
export default {
methods: {
useExternalModule() {
externalModule.doSomethig();
}
}
};
Is it possible to mock the externalModule inside the story?
I'm using Storybook v6.

You can create a __mocks__ folder to put your mock components in. Then in your .storybook/main.js file use this to point webpack to your mock file.
module.exports = {
// your Storybook configuration
webpackFinal: (config) => {
config.resolve.alias['externalModule'] = require.resolve('../__mocks__/externalModule.js');
return config;
},
};
This is covered in the docs under "Mocking imports".
However, this is a global configuration and not a story level configuration.

Related

Get config value in vue file

possibly a very simple question: I need to get a configuration value from within an html block in a vue file.
I have this simple config.js
const env = process.env.NODE_ENV
const development = {
images: {
server: "http://localhost:4001",
}
}
const production = {
images: {
server: "http://someimageserver.com",
}
}
const config = {
development,
production,
}
module.exports = config[env]
And this simple vue.js
<template>
<div>
<img :src="`${config.images.server}/images/someimage.jpg`"/>
</div>
</template>
At run time, the above throws
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'images')
What should I do to make this work ?
Thanks in advance
Note: I can get configuration values from within the script block using, this works perfectly, for example
import config from "../../config"
...
var c = config.images.server
UPDATE:
Using vue 3, one can easily achieve this by adding
import config from "../config"
app.config.globalProperties.$config = config
to the main.js file. From there on, $config can be used in templates and scripts across all files. Source: https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-prototype-replaced-by-config-globalproperties
In Vue, you need to initiate a variable and assign what you imported to it, and eventually return this variable. It looks like below:
Vue2:
import config from "../../config"
export default {
data() {
return {
config: config
}
}
}
Vue3:
import config from "../../config"
export default {
setup() {
return {
config
}
}
}
Then the url in the template should work fine.
-------------------------updates-----------------------
If you want to use config globally, you can register it as a Plugin.
Create plugin.js
import config from "../../config"
export const Config = {
install(Vue, options) {
Vue.prototype.$config = function() {
return config
}
}
}
Then, in your main.js, add below code
import * as Plugins from '#/plugin.js'
Vue.use(Plugins.Config.install)
Then you can use $config within templetes like $route without any other import. Surely you can write other global functions in plugin.js and register each of them in main.js.

How to add included Pug files to Vite module graph

I wrote a Rollup plugin to import Pug as an HTML string:
// Rollup plugin imported to Vite config
import { render } from 'pug';
export default function pug() {
return {
name: 'rollup-plugin-pug-html',
transform(src, id) {
if (id.endsWith('.pug')) {
const html = render(src, { filename: id });
const code = `export default ${JSON.stringify(html)};`;
return { code };
}
},
};
}
I'm using it in Vite to create templates for Vue components, as in this reduced example:
// ProofOfConceptSFC.vue
<script>
import { compile } from 'vue/dist/vue.esm-bundler.js';
import template from './template.pug';
export default {
render: compile(template)
};
</script>
The HMR is working great when I edit template.pug. The new template appears and the latest reactive values persist.
My problem is that template.pug may depend on other Pug files with include:
//- template.pug
include ./header.pug
p Hello {{ name }}
include ./footer.pug
The Vite server doesn't know about those files, and nothing happens if I touch them. Ideally I could invalidate template.pug when any Pug file is changed.
I'm guessing I want my plugin to update the ViteDevServer's server.moduleGraph. Is there a supported way to do that?
Huge thanks to the friendly Vite chat on Discord for setting me in the right direction.
The two keys I was missing:
Use Pug compile to create a render method that has render.dependencies, as done by Parcel
Use virtual import statements to attach the dependencies to the transform hook result, as done by vite-plugin-svelte.
Here is the working plugin:
import { compile } from 'pug';
export default function pluginPug() {
return {
name: 'vite-plugin-pug',
transform(src, id) {
if (id.endsWith('.pug')) {
const render = compile(src, { filename: id });
const html = render();
let code = '';
for (let dep of render.dependencies) {
code += `import ${JSON.stringify(dep)};\n`;
}
code += `export default ${JSON.stringify(html)};`;
return { code };
}
},
};
}

Access Nuxt custom plugin from Composition API

I am using VueClipboard in my nuxt project.
https://www.npmjs.com/package/vue-clipboard2
I have a plugin file vue-clipboard.js
import Vue from "vue";
import VueClipboard from 'vue-clipboard2';
Vue.use(VueClipboard);
It is imported into nuxt.config
plugins: ['#/plugins/vue-clipboard'],
This sets up a global variable $copyText and in nuxt without the composition API I can do something like
methods: {
async onCopyCodeToClipboard() {
const code = 'code'
await this.$copyText(code)
},
},
However inside the setup using the composition API (#nuxtjs/composition-api) when I write a function I do not have access to this.$copyText
const onCopyCodeToClipboard = async () => {
const code = context.slots.default()[0].elm.outerHTML
// -> Can't use this here - await this.$copyText(code)
}
So how do I make $copyText available to use inside the composition API?
I was able to get this to work via the Nuxt useContext() method:
import { useContext } from '#nuxtjs/composition-api'
export default function () {
const { $copyText } = useContext();
$copyText('code');
}

Vue.js / Mixins - Is there a way to get the global mixin-object outside of the vue component?

I am new with Vue.js
I am using Vue.js 2.4.4.
I have created the global mixin in my app.js file:
...
import router from './router'
...(some imports and plugins definitions)
Vue.use(VeeValidate);
Vue.component(VuePassword);
...
Vue.mixin({
data: function(){
return {
get auth(){
return Auth;
}
}
}
});
const app = new Vue({
el: '#root',
template: `<app></app>`,
components: { App },
router
});
This mixin imports some Auth object with validation methods e.t.c which needed to be in every component.
All of my components can check this mixin and it's working fine.
But I need to check the auth state after every route request, and I want to use my currently existing mixin, so I am trying to make something like this in my router.js file:
import Vue from 'vue'
import VueRouter from 'vue-router'
...
Vue.use(VueRouter);
const router = new VueRouter({
routes:[
...
]
});
router.beforeEach((to, from, next) => {
if(to.meta.requiresAuth) {
if(...call to mixin method) {
next();
} else {
next('/');
}
} else {
next();
}
});
export default router
Question:
Is there a way to get the global mixin object and change it's inner values or can you please give some small advise or example what is the right solution to this kind of tasks?
Or should I use the plugins instead of mixins?
I would rather create a seperate file for auth and not make it a mixin. Then using Vue.use() which will set auth on the vue object.
A sample of what the files might look like:
auth.js
export default function(Vue) {
Vue.auth = {
// do your auth logic
}
}
Then in your main js file
main.js
import Auth from './auth.js'
Vue.use(Auth);
Then you should be able to use Vue.auth
Another option would be keep using the mixin and pass the value to a store (like vuex) or create your own if your project is small...

How to use own JS as a plugin using Nuxt.js

I am using nuxt.js. I have a helper.js script inside plugins folder which has a simple Test() function. Now how can I can call the Test() method inside pages which is in helper.js file.
helper.js file:
export default function Test() {
return 'This is test'
}
to access your global methods entire application:
1-create ./plugins/helpers.js .
2-edit ./plugins/helpers.js :
import Vue from 'vue'
Vue.mixin({
methods:{
mySpecialMethod(value){
console.log(value)
},
}
})
3-edit ./nuxt.config.js :
plugins: [
...
{ src: '~/plugins/helpers' },
...
],
now you can access your global method by:
this.mySpecialMethod()
Using the inject method
There is actually an easy way to do this by using the 'inject' method.
As described in the docs...
The plugins directory contains JavaScript plugins that you want to run before instantiating the root Vue.js Application. This is the place to add Vue plugins and to inject functions or constants. Every time you need to use Vue.use(), you should create a file in plugins/ and add its path to plugins in nuxt.config.js.
in your plugin simply use inject like this:
export default ({ app }, inject) => {
inject('myInjectedFunction', (string) => console.log('That was easy!', string))
}
and in your components you can use it as follows:
export default {
mounted(){
this.$myInjectedFunction('works in mounted')
},
asyncData(context){
context.app.$myInjectedFunction('works with context')
}
}
"Manual" injection
If you plan on injecting something yourself check out the Vue Docs on Adding Instance properties
There may be data/utilities you’d like to use in many components, but you don’t want to pollute the global scope. In these cases, you can make them available to each Vue instance by defining them on the prototype
Vue.prototype.$appName = 'My App'
And prefix these injected properties with '$'...
$ is a convention Vue uses for properties that are available to all instances. This avoids conflicts with any defined data, computed properties, or methods.
If you just want to use the code in your components (pages), you only need to import and use the method:
TestPage.vue
<template>
<div>
<h1>{{ getTest }}</h1>
</div>
</template>
<script>
import test from '~/plugins/helper.js'
export default {
computed: {
getTest () {
return test()
}
}
}
</script>
Hello you can inject the function globally into Vue doing the following:
./plugins/myPluging.js
import Vue from 'vue'
Vue.prototype.$nameOfMyPlugin = (args) => {
// Code here
}
Them in all your components you can access it this way:
./components/myComponent.vue
<script>
export default {
name: 'c',
mounted () {
this.$nameOfMyPlugin('something useful')
}
}
</script>
And that's it :) hope this helps.
-- Reference: https://nuxtjs.org/guide/plugins/#inject-in-root-amp-context
Below is a a custom js plugin that I have used in one of my nuxt projects.
create your file inside the plugins folder, and make your own function as below
export default (context, inject) => {
const formatDate = (dateTime) => {
if (typeof(dateTime) === 'undefined' || dateTime === null) {
return null;
}
let tempDate = new Date(dateTime);
tempDate.setMinutes(tempDate.getMinutes() -
tempDate.getTimezoneOffset());
tempDate = tempDate.toISOString().slice(0, 16);
return tempDate;
}
// Inject $hello(msg) in Vue, context and store.
inject('formatDate', formatDate)
// For Nuxt <= 2.12, also add 👇
context.$formatDate = formatDate
}
Add the plugin to nuxt.config.js and you will be able to use it globally.
myPlugin.js
export default (_, inject) => {
const myFuncA = value => return value;
const myFuncB = value => return myFuncA(1) + value;
inject('myPlugin', { myFuncA, myFuncB }
)
nuxt.config.js
plugins[
'#/plugin/myPlugin.js'
]
myComponent.vue
created(){
console.log( this.$myPlugin.funcA(2) );
}
in myPlugin.js, instead of "_" can use some public nuxt variables like {$config}