Vuetify Storybook remapInternalIcon issue - vuejs2

Using Vuetify 2 and Storybook 6 (source https://github.com/lydonchandra/vuetify2storybook6 )
The component renders fine, but keep getting this error TypeError because vm.$vuetify.icons is undefined, when rendering component for first time.
Not sure which storybook-vuetify initialization bridge did I miss ?
TypeError: Cannot read property 'component' of undefined
at remapInternalIcon (vuetify.js:44048)
at VueComponent.getIcon (vuetify.js:16881)
at Proxy.render (vuetify.js:17009)
at VueComponent.Vue._render (vue.esm.js:3557)
at VueComponent.updateComponent (vue.esm.js:4075)
at Watcher.get (vue.esm.js:4488)
at new Watcher (vue.esm.js:4477)
function remapInternalIcon(vm, iconName) {
// Look for custom component in the configuration
var component = vm.$vuetify.icons.component; // <-- issue here when rendering for first time
if (iconName.startsWith('$')) {
// Get the target icon name
src/plugins/vuetify.ts
import Vue from "vue";
import Vuetify from "vuetify/lib";
import { UserVuetifyPreset } from "vuetify";
Vue.use(Vuetify);
export const options: UserVuetifyPreset = {
icons: {
iconfont: "mdiSvg"
}
};
export default new Vuetify(options);

Workaround for now is to set addon-essentials.docs to false. (Ref.
https://github.com/storybookjs/storybook/issues/7593)
file: .storybook/main.js
...
"addons": [
"#storybook/addon-links",
{
name: "#storybook/addon-essentials",
options: {
docs: false
}
}
],
...

If you don't want to disable addon-essentials.docs, you can add the following style in .storybook/preview-head.html
<style>
.sb-errordisplay {
display: none !important;
}
</style>

Another workaround without having to disable addon-essentials or adding any styles in the preview-head.html file you can import Vuetify at the top of your .stories.js (or .stories.ts) file like so e.g.
import vuetify from '#/plugins/vuetify'
then when you declare your storybook Template, pass in your vuetify object
const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { YourComponent },
vuetify, // <-- Very important line
template: `<YourComponent />`
})
I found this workaround in this thread Cannot read property 'mobile' of undefined - Vue/Vuetify/Storybook

Related

How can i make all v-text-field components outlined by default in nuxt/vuetify module

i'm using the nuxt/vuetify module and would like to make all v-text-fields components outlined.
Try to create and register plugin which register new vue component, that extends vuetify VTextField component.
import Vue from 'vue';
import { VTextField } from "vuetify/lib"
Vue.component('mTextField', {
extends: VTextField,
props: {
outlined: {
type: Boolean,
default: true
}
}
})
But always catch error while try to use mTextField component
Unexpected token 'export'
How can i make all v-text-fields components outlined?
Add transpile section in nuxt.config.js with 'vuetify/lib' worked for me
build: {
transpile: ['vuetify/lib']
},

Conditionally import a component in Vue Router

I'd like to conditionnaly import a component in the vue router. Here is what I have for the moment:
children: [
{
path: ':option',
component: () => import('../components/Option1.vue'),
},
],
Depending on what :option is, I want to import a different component (Option1.vue, Option2.vue, etc.). I know I could put several children but i actually need the option variable in my parent component (I make tests if the route has an option).
How would it be possible to do that?
Thanks in advance :)
You can create a loader component containing a dynamic component instead of doing conditional routing. In the loader, you'll conditionally lazy load the option component based on the route param. Not only is this easier when routing, you also don't have to manually import anything, and only options that are used will be imported.
Step 1. Route to the option loader component
router
{
path: ':option',
component: () => import('../components/OptionLoader.vue'),
}
Step 2. In that option loader template, use a dynamic component which will be determined by a computed called optionComponent:
OptionLoader.vue
<template>
<component :is="optionComponent" />
</template>
Step 3. Create a computed that lazy loads the current option
OptionLoader.vue
export default {
computed: {
optionComponent() {
return () => import(`#/components/Option${this.$route.params.option}.vue`);
}
}
}
This will load the component called "Option5.vue", for example, when the option route param is 5. Now you have a lazy loaded option loader and didn't have to manually import each option.
Edit: OP has now indicated that he's using Vue 3.
Vue 3
For Vue 3, change the computed to use defineAsyncComponent:
OptionsLoader.vue
import { defineAsyncComponent } from "vue";
computed: {
optionComponent() {
return defineAsyncComponent(() =>
import(`#/components/Option${this.$route.params.option}.vue`)
);
}
}
Here is something that works in VueJS3:
<template>
<component :is="userComponent"/>
</template>
<script>
import { defineAsyncComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
export default {
computed: {
userComponent() {
const route = useRoute();
const router = useRouter();
const components = {
first: 'Option1',
second: 'Option2',
third: 'OtherOption',
fourth: 'DefaultOption',
};
if (components[route.params.option]) {
return defineAsyncComponent(() => import(`./options/${components[route.params.option]}.vue`));
}
router.push({ path: `/rubrique/${route.params.parent}`, replace: true });
return false;
},
},
};
</script>
Source: https://v3-migration.vuejs.org/breaking-changes/async-components.html
And it's possible to get an error message like this one for the line with "return":
Syntax Error: TypeError: Cannot read property 'range' of null
In that case, it means you probably want to migrate from babel-eslint to #babel/eslint-parser (source: https://babeljs.io/blog/2020/07/13/the-state-of-babel-eslint#the-present)

using vue-chartjs in vue 3 : createElement is not a function

I'm using Vue.js 3 and I can't make a chart with Vue-chartjs because of this error:
Uncaught TypeError: createElement is not a function
at Proxy.render (BaseCharts.js?86fc:8)
at renderComponentRoot (runtime-core.esm-bundler.js?5c40:673)
at componentEffect (runtime-core.esm-bundler.js?5c40:4475)
at reactiveEffect (reactivity.esm-bundler.js?a1e9:42)
at effect (reactivity.esm-bundler.js?a1e9:17)
at setupRenderEffect (runtime-core.esm-bundler.js?5c40:4458)
at mountComponent (runtime-core.esm-bundler.js?5c40:4416)
at processComponent (runtime-core.esm-bundler.js?5c40:4376)
at patch (runtime-core.esm-bundler.js?5c40:3991)
at mountChildren (runtime-core.esm-bundler.js?5c40:4180)
this is App.vue that displays my chart:
<template>
<line-chart />
</template>
<script>
import LineChart from "./components/Chart";
export default {
name: "App",
components: {
LineChart
}
};
</script>
and this is Chart.vue that renders a line chart :
<script>
import { Line } from "vue-chartjs";
export default {
extends: Line,
data: () => ({
chartdata: {
labels: ["January", "February"],
datasets: [
{
label: "Data One",
backgroundColor: "#f87979",
data: [40, 20]
}
]
},
options: {
responsive: true,
maintainAspectRatio: false
}
}),
mounted() {
this.renderChart(this.chartdata, this.options);
}
};
</script>
I have tried this with various forms of data, but apparently, the problem is elsewhere.
Do I have to wait for the vue.js 3 ecosystem to become more complete?
Update 2022
The library supports vue 3 now and you can install as follows :
pnpm add vue-chartjs chart.js
# or
yarn add vue-chartjs chart.js
# or
npm i vue-chartjs chart.js
Old answer
According to this issue this library doesn't support Vue 3 yet, and the origin of this error could explained here :
in vue 2 we do the following to create a render function :
export default {
render(createElement ) { // createElement could be written h
return createElement('div')
}
}
in Vue 3 :
import { h } from 'vue'
export default {
render() {
return h('div')
}
}
which means that createElement is undefined
https://github.com/apertureless/vue-chartjs
Vue Charts does not seem to be ready for vue3
Compatibility
v1 later #legacy
Vue.js 1.x
v2 later
Vue.js 2.x
Discussion about vue3 here: https://github.com/apertureless/vue-chartjs/issues/601
and here: https://github.com/apertureless/vue-chartjs/issues/637

Gridsome Full Calendar build error - no SSR

I'm trying to use the Full Calendar vue component (https://github.com/fullcalendar/fullcalendar-vue) in a Gridsome project like so:
<template>
<div class="tabStaffManage">
<div>
<FullCalendar
ref="staffCalendar"
class="fullCalendar"
defaultView="dayGridMonth"
:events="calendarEvents"
:plugins="calendarPlugins"
:allDaySlot="false"
:header="{
center: 'dayGridMonth, timeGridDay',
right: 'prev, next'
}"
minTime="09:00:00"
:selectable="true"
maxTime="18:30:00"
#eventClick="onEventClick"
#select="onDateSelect"
:showNonCurrentDates="false"
></FullCalendar>
</div>
</div>
</template>
<script>
import { formatDate } from "#fullcalendar/core"
import FullCalendar from "#fullcalendar/vue"
import timeGridPlugin from "#fullcalendar/timegrid"
import dayGridPlugin from "#fullcalendar/daygrid"
import interactionPlugin from "#fullcalendar/interaction"
export default {
components: {
FullCalendar,
},
data() {
return {
calendarPlugins: [dayGridPlugin, timeGridPlugin, interactionPlugin],
}
},
}
</script>
This, however, produces an error on build:
Could not generate HTML for "/staff/dashboard/":
ReferenceError: Element is not defined
at Object.338 (node_modules/#fullcalendar/core/main.esm.js:102:0)
at __webpack_require__ (webpack/bootstrap:25:0)
at Module.552 (assets/js/page--src-pages-staff-dashboard-vue.ea5234e7.js:598:16)
at __webpack_require__ (webpack/bootstrap:25:0)
I understand that Full Calendar does not support SSR. So as per the Gridsome documentation (https://gridsome.org/docs/assets-scripts/#without-ssr-support) I did this to import the component:
I created an alias for it's dependencies in gridsome.config.js like so:
var path = require('path');
api.configureWebpack({
resolve: {
alias: {
"timeGridPlugin": path.resolve('node_modules', '#fullcalendar/timegrid'),
etc....
}
},
})
and required those plugins in the mounted() lifecycle hook:
mounted() {
if (!process.isClient) return
let timeGridPlugin = require('timeGridPlugin')
...
},
components: {
FullCalendar: () =>
import ('#fullcalendar/vue')
.then(m => m.FullCalendar)
.catch(),
}
I then wrapped the FullCalendar component in:
<ClientOnly>
<FullCalendar></FullCalendar>
</ClientOnly>
The extra dependencies required in the mounted() hook are included no problem.
However I now get the following error:
TypeError: Cannot read property '__esModule' of undefined
It seems that components() is failing to import the '#fullcalendar/vue' component.
Am I doing something wrong when importing the '#fullcalendar/vue' component?
Is there another way to include both the '#fullcalendar/vue' component and the plugin dependencies with no SSR?
Requiring the full calendar vue component in main.js by checking the gridsome client API and registering the component globally in vue seems to work and does what I expected:
// Include no SSR
if (process.isClient) {
const FullCalendar = require("#fullcalendar/vue").default
Vue.component("full-calendar", FullCalendar)
}
I also was not pointing to the default object when requiring the other modules in the component:
mounted() {
if (!process.isClient) return
let timeGridPlugin = require('timeGridPlugin').default
...
}

How to inject Vuetify into my custom vue plugin

I would like to create a Vue plugin with a function which programatically renders a Vue component. That component depends on Vuetify. Everything works fine if I use vanilla HTML/CSS in that component, but using Vuetify-related things in there (e.g. a ) does not work. I assume that I didn't inject vuetify itself into the component correctly.
In my custom component, I tried importing every Vuetify component separately, but without success. I also tried creating the component with the syntax: new Vue({vuetify}), but also without success.
import MyCustomComponent from '#/components/MyCustomComponent'
import vuetify from '#/plugins/vuetify';
export default {
install(Vue, options) {
function renderMyCustomComponent() {
const CustomComponent= Vue.extend(MyCustomComponent)
Vue.use(vuetify)
let instance = new CustomComponent()
instance.$mount()
document.body.appendChild(instance.$el)
}
Vue.prototype.$renderMyComponent = renderMyCustomComponent
}
}
The error message indicates, that vuetify (or at least some of it's properties) are not available in my component
[Vue warn]: Error in getter for watcher "isDark": "TypeError: Cannot read property 'dark' of undefined"
HINT/EDIT: I am using Vuetify 2.0. The way Vuetify is injected into the app changed a little bit. Here's the code of my vuetify plugin file:
import Vue from 'vue';
import Vuetify from 'vuetify';
import 'vuetify/dist/vuetify.min.css';
import de from 'vuetify/es5/locale/de';
Vue.use(Vuetify)
export default new Vuetify({
theme: {
themes: {
light: {
primary: '#3f51b5',
secondary: '#b0bec5',
accent: '#8c9eff',
error: '#b71c1c'
},
},
},
});
Not sure if you solved this issue, but I had the same problem where Vuetify in a plugin would not be initialized correctly.
Vuetify documentation states that you need to define a vuetify option when creating your vue instance:
new Vue({
vuetify,
}).$mount('#app')
Fortunately, custom Vue plugins has an options parameter that we can use.
Here is the code that consumes your plugin:
const options = {}; // add other options here! (vuex, router etc.)
Vue.use(YourCustomPlugin, options);
new Vue(options).$mount('#app');
And here is your plugin code:
import vuetify from "./src/plugins/vuetify";
export default {
install(Vue, options) { // options is undefined unless you pass the options param!
Vue.component('my-custom-component', MyCustomComponent);
Vue.use(Vuetify);
options.vuetify = vuetify;
}
};
The vuetify module is very simple:
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
const opts = {}
export default new Vuetify(opts);
The problem is that you actualy don't export plugin itself in '#/plugins/vuetify';
import MyCustomComponent from '#/components/MyCustomComponent'
import Vuetify from 'vuetify';
export default {
install(Vue, options) {
function renderMyCustomComponent() {
Vue.use(Vuetify)
const CustomComponent= Vue.extend(MyCustomComponent)
let instance = new CustomComponent()
instance.$mount()
document.body.appendChild(instance.$el)
}
Vue.prototype.$renderMyComponent = renderMyCustomComponent
}
}