Storybook doesn't find dynamic story args - vue.js

I recently upgraded my Nuxt 2 project to use Yarn 2 and tweak some dependencies and I looks like Storybook doesn't work with dynamic props as it used to.
Here are the packages that I have installed
"#storybook/addon-essentials": "6.4.19",
"#storybook/addon-postcss": "2.0.0",
"#nuxtjs/storybook": "4.3.0",
LayoutFooter.stories.mdx
import { Meta, Story, Canvas } from '#storybook/addon-docs';
import LayoutFooter from '~/components/layout/LayoutFooter';
<Meta title="Layout/LayoutFooter" component="{LayoutFooter}" />
# Footer
`Footer` component
## Props
<ArgsTable of={LayoutFooter} />
## Example
export const Template = (_args, { argTypes }) => ({
props: Object.keys(argTypes),
template: `<LayoutFooter :logo="logo" />`,
components: { LayoutFooter },
});
Footer
<Canvas>
<Story name="Default" args={{ logo: 'img/logo.svg' }}>
{Template.bind({})}
</Story>
</Canvas>
Also tried
export const Template = (_args, { argTypes }) => ({
props: Object.keys(argTypes),
template: `<LayoutFooter v-bind="$props" />`,
components: { LayoutFooter },
});
I get this error
vue.js:634 [Vue warn]: Invalid prop: type check failed for prop "logo". Expected String, got Undefined
found in
---> <LayoutFooter> at components/layout/LayoutFooter.vue
<Anonymous>
<Anonymous>
<Root>
Am I doing something wrong with the syntax?

Related

Asynchronously modify value of component in Quasar

I am trying to modify the alias field when a promise is resolved. When I try to await the promise, Quasar errors out with:
[Vue warn]: Component <MainLayout>: setup function returned a promise, but no <Suspense>
boundary was found in the parent component tree. A component with async setup() must be
nested in a <Suspense> in order to be rendered.
I tried wrapping everything in the <Suspense> tag including the individual spot I'm awaiting that data, but I still get this error.
I'm trying to promisify a GUN DB event that resolves a user's alias by pubkey.
<template>
<Suspense>
<q-layout view="lHh Lpr lFf">
<q-header elevated>
<q-toolbar>
<q-btn
flat
dense
round
icon="menu"
aria-label="Menu"
#click="toggleLeftDrawer"
/>
<q-toolbar-title> Quasar App </q-toolbar-title>
<div>{{ alias }}</div>
</q-toolbar>
</q-header>
<q-drawer v-model="leftDrawerOpen" show-if-above bordered>
<q-list>
<q-item-label header> Essential Links </q-item-label>
<EssentialLink
v-for="link in essentialLinks"
:key="link.title"
v-bind="link"
/>
</q-list>
</q-drawer>
<q-page-container>
<router-view />
</q-page-container>
</q-layout>
</Suspense>
</template>
<script>
import { defineComponent, ref } from "vue";
import EssentialLink from "components/EssentialLink.vue";
import { GUN } from "boot/gun";
const gun = Gun();
const user = gun.user().recall({ sessionStorage: true });
const linksList = [
{
title: "Docs",
caption: "quasar.dev",
icon: "school",
link: "https://quasar.dev",
},
];
export default defineComponent({
name: "MainLayout",
components: {
EssentialLink,
},
async setup() {
const leftDrawerOpen = ref(false);
let alias = "Getting alias";
const pubkey = JSON.parse(sessionStorage.getItem("pair")).pub;
alias = new Promise((resolve, reject) => {
gun.user(pubkey).once((data) => resolve(data.alias));
});
return {
alias,
essentialLinks: linksList,
leftDrawerOpen,
toggleLeftDrawer() {
leftDrawerOpen.value = !leftDrawerOpen.value;
},
};
},
});
</script>
What is the proper way to await this data and update it in Quasar?
You could move the async operation to the mounted hook. Also, because you want alias to reactively update on the UI you should wrap it in a ref() when initializing. I've provided simplified code below showing how it can be done:
<template>
<div>{{ alias }}</div>
</template>
<script>
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
const alias = ref("Getting alias");
return {
alias,
};
},
async mounted() {
this.alias = await new Promise((resolve, reject) => {
gun.user(pubkey).once((data) => resolve(data.alias));
});
},
});
</script>
example codesandbox

ReferenceError: computed is not defined on Vitest test suite

Description
I'm migrating test suites from Jest to Vitest.
But i've a problem when i run test suites, an error occurs when a component has a computed property.
The common error is :
ReferenceError: computed is not defined
- /components/Ui/Avatar.vue:13:30
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:157:22
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:7084:29
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:7039:11
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:5401:13
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:5376:17
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:4978:21
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:5515:21
- /node_modules/#vue/reactivity/dist/reactivity.cjs.js:189:25
- /node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:5622:56
Versions
"vitest": "^0.18.1"
"jsdom": "^20.0.0"
"#vue/test-utils": "^2.0.2"
Exemple
Here is my component code :
<template>
<image
:src="src"
:onerror="onErrorLoadImage"
:class="['avatar', { big }]"
/>
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
src?: string
big?: boolean
errorImage?: string
}>(), {
src: '',
big: false,
errorImage: '/no-avatar.png',
})
const onErrorLoadImage = computed(() => `this.src='${props.errorImage}';this.onerror='';`)
</script>
And my test
import { describe, it, expect } from 'vitest'
import { mount } from '#vue/test-utils'
import UiAvatar from './Avatar.vue'
const componentName = 'img'
const src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII='
const big = true
const errorImage = '/no-avatar.png'
describe('UiAvatar', () => {
it('should be render the component', () => {
const wrapper = mount(UiAvatar, {
propsData: {
src,
big,
errorImage
}
})
expect(wrapper.element.tagName).toBe(componentName)
})
})
Thanks :)
This can be solved by using the following npm packages:
unplugin-vue-components/vite
unplugin-auto-import/vite
They only need to specify a path where the import files will be generated, in my case I use a storybook folder since I also use the plugin there, but it can be any other path
vitest.config.ts

Vue Component including Inertiajs-Link Not working in Project

I'm going to build small UI library package with Vue components and use it in my Inertia-Laravel Project.
//Logo.vue
<template>
<Link href="/" class="text-xl font-bold flex items-center lg:ml-2.5">
My Logo
</Link>
</template>
<script>
import { Link } from '#inertiajs/inertia-vue3'
export default {
name: "Logo",
components: {
Link,
},
}
</script>
I was able to build this as package Vite or Vue-SFC-RollUp and publish it on npm.
But when I was going to install it on my inertia/laravel projects and use it, I got some warning and error.
MyProjectComponent.vue
<template>
...
<Logo />
...
</template>
<script>
import {Logo} from 'mypackage-ui'
export default {
components: {Logo}
}
</script>
Error message:
export 'default' (imported as 'require$$1') was not found in 'vue'
(possible exports: BaseTransition, Comment, EffectScope, ... , withScopeId)
If I remove <Link> in Logo.vue and use <a> tag and update package, then it's working well.
Any suggestion would be highly appreciated.
I'm using Vue 3.
The solution to this is to add the inertia link as a component in the app.js file:
import { createInertiaApp, Head, Link } from '#inertiajs/inertia-vue3';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.component('InertiaHead', Head)
.component('InertiaLink', Link)
.use(ZiggyVue, Ziggy)
.mount(el);
},
});

vue.js vue-gl missing props camera in component

I installed Vue-gl in my app.js as
const { VglRenderer, VglScene } = require('vue-gl');
Object.keys(VueGL).forEach((name) => Vue.component(name, VueGL[name]));
Then in my parent component I have a child component:
<scene :positionItem="positionItem"></scene>
import scene from "./../components/scene";
Having this code:
export default {
name: "configurator",
components :{
scene
},
data: function(){
return {
positionItem: -4,
}
},
methods:{
updatePosition(){
this.$root.$emit("input", {
positionItem :this.$refs.positionItem.value
})
}
},
mounted() {
this.$root.$on('input', data => {
this.positionItem = data;
});
}
}
The child component "scene" is
<template>
<vgl-renderer >
<vgl-sphere-geometry name="sphere"></vgl-sphere-geometry>
<vgl-scene name="main">
<!-- Re-rendering will be triggered when zCoordinate changes. -->
<vgl-mesh :position="`0 0 ${positionItem}`" geometry="sphere"></vgl-mesh>
</vgl-scene>
<vgl-perspective-camera orbit-position="12 0 0" name="camera"></vgl-perspective-camera>
</vgl-renderer>
<script lang="ts">
export default {
props: ["positionItem"],
data: function () {
return {
}
},
}
</script>
The renders works but I got 2 errors about missing props camera and scene:
vue.esm.js?efeb:628 [Vue warn]: Missing required prop: "camera"
found in
--->
at src/components/scene.vue
at src/components/configurator.vue
at src/App.vue
How to fix it?
I had the exact same problem. The vgl-renderer component needs to be given a name and a screne property of type string. These components must be present in the scene with the same names. Otherwise vue-gl will throw another error. I can't find anything about these required properties on the vue-gl documentation. Maybe it's outdated. I managed to get it working like this ...
<template>
<vgl-renderer
camera= "camera"
scene= "scene"
class="getting-started"
>
<vgl-box-geometry name="box"></vgl-box-geometry>
<vgl-scene name="scene">
<vgl-mesh geometry="box"></vgl-mesh>
</vgl-scene>
<vgl-perspective-camera name="camera" orbit-position="3 1 0.5"></vgl-perspective-camera>
</vgl-renderer>
</template>
<script>
const { VglRenderer, VglScene, VglBoxGeometry, VglMesh, VglPerspectiveCamera } = require('vue-gl');
export default {
components : {
VglRenderer, VglScene, VglBoxGeometry, VglMesh, VglPerspectiveCamera
}
}
</script>

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
...
}