Writing documentation for plain JS class in storybook - javascript

I have just started using Storybook for a UI component lib I am working on. I wanted to extract JSDoc written for JS class methods and properties into Storybook and create a Doc.
Storybook does support creating doc for React components by reading its propTypes. Is there addon or someway to do the same for a JS class.
I am using the latest storybook 6.
Thanks in advance

You can do it like a normal component:
form-validators.stories.ts
import { FormValidators } from './path';
export default {
title: 'Components/Form Validators',
component: FormValidators,
parameters: {
previewTabs: { canvas: { hidden: true } },
docsOnly: true,
},
} as Meta;
export const Default: Story = () => ({
template: '<div>Test</div>',
});
Or I prefer an MDX file.
form-validators.stories.mdx
import { ArgsTable } from '#storybook/addon-docs/blocks';
import { Meta } from '#storybook/addon-docs/blocks';
import { FormValidators } from './path';
<Meta
title="Components/Form Validators"
parameters={{ previewTabs: { canvas: { hidden: true } } }}
/>
<ArgsTable of={FormValidators} />

Related

How to use Vue 3 Meta with Vue.js 3?

It seems that Vue Meta has been upgraded to handle Vue.js 3 with a new npm package called vue-3-meta
Before Vue.js 3, it was easy to use vue-meta by adding it to the Vue instance:
import Vue from 'vue'
import VueMeta from 'vue-meta'
Vue.use(VueMeta, {
// optional pluginOptions
refreshOnceOnNavigation: true
})
However in Vue.js 3, there is no Vue instance; and instead you create the app by running createApp like such:
const app = createApp(App);
const router = createVueRouter();
app.use(router);
// need to make app use Vue-Meta here
I cannot find any documentation for vue-3-meta. import VueMeta from 'vue-meta' no longer works.
How do I import the vue-3-meta plugin properly and use it with app like in prior versions?
Disclaimer: vue-meta v3 is still in alpha!
This was the minimal implementation I needed to get started:
Update vue-meta to v3 (in package.json)
- "vue-meta": "^2.4.0",
+ "vue-meta": "^3.0.0-alpha.7",
...or with yarn:
yarn add vue-meta#alpha
Add metaManager to Vue app
import { createMetaManager } from 'vue-meta'
const app = createApp(App)
.use(router)
.use(store)
.use(createMetaManager()) // add this line
await router.isReady()
app.mount('#app')
Add <metainfo> to App.vue <template> (this is also where I set a "title template")
<template>
<metainfo>
<template v-slot:title="{ content }">{{ content ? `${content} | SITE_NAME` : `SITE_NAME` }}</template>
</metainfo>
<header />
<router-view />
<footer />
</template>
Set default meta in App.vue <script>
Vue 3 vanilla:
import { useMeta } from 'vue-meta'
export default {
setup () {
useMeta({
title: '',
htmlAttrs: { lang: 'en', amp: true }
})
}
}
or with vue-class-component:
import { setup, Vue } from 'vue-class-component'
import { useMeta } from 'vue-meta'
export default class App extends Vue {
meta = setup(() => useMeta({
title: '',
htmlAttrs: { lang: 'en', amp: true }
})
}
Override meta in each component
Vue 3 vanilla:
import { useMeta } from 'vue-meta'
export default {
setup () {
useMeta({ title: 'Some Page' })
}
}
or with vue-class-component:
import { computed } from '#vue/runtime-core'
import { setup, Vue } from 'vue-class-component'
import { useMeta } from 'vue-meta'
export default class SomePage extends Vue {
meta = setup(() => useMeta(
computed(() => ({ title: this.something?.field ?? 'Default' })))
)
}
See also:
"Quick Usage" (vue-meta next branch)
Vue Router Example (vue-meta next branch)
In addition to the previous answers, I also needed to add a transpileDependency in my vue.config.js, as I was using vue-cli:
module.exports = {
transpileDependencies: ['vue-meta']
}
Else, I would get the error:
error in ./node_modules/vue-meta/dist/vue-meta.esm-browser.min.js
Module parse failed: Unexpected token (8:7170)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
Thanks to this thread for pointing me to this: https://stackoverflow.com/a/65844988/3433137
metaManager is a MetaManager instance created from createMetaManager() of vue-meta.
Based on the Vue 3 + Vue Router example for vue-meta, here's an example usage:
import { createApp } from 'vue'
import { createMetaManager, defaultConfig, resolveOption, useMeta } from 'vue-meta'
const decisionMaker5000000 = resolveOption((prevValue, context) => {
const { uid = 0 } = context.vm || {}
if (!prevValue || prevValue < uid) {
return uid
}
})
const metaManager = createMetaManager({
...defaultConfig,
esi: {
group: true,
namespaced: true,
attributes: ['src', 'test', 'text']
}
}, decisionMaker5000000)
useMeta(
{
og: {
something: 'test'
}
},
metaManager
)
createApp(App).use(metaManager).mount('#app')

How to separate bundles in StencilJS for third-party libraries

I am going to use third-party web components library in a Stencil project.
But when I do this, all dependent code are included into bundle with my stencil component and is a big size.
How to separate third-party components/modules code in stencil build?
import { Component, h } from '#stencil/core';
import { ModuleManager } from 'igniteui-webcomponents-core';
import { IgcDataGridModule } from 'igniteui-webcomponents-grids';
import { IgcDataGridComponent } from 'igniteui-webcomponents-grids';
#Component({
tag: 'test-component',
styleUrl: 'test-component.css',
shadow: true,
})
export class TestComponent {
data: Array<Object>;
grid: IgcDataGridComponent;
constructor() {
ModuleManager.register(
IgcDataGridModule
);
}
componentDidRender() {
this.grid.dataSource = this.data;
}
render() {
...
return (
<div>Test component
<igc-data-grid ref={el => this.grid = el as IgcDataGridComponent}
height="100%"
width="100%"
auto-generate-columns="true"
default-column-min-width="100"
summary-scope="Root"
is-column-options-enabled="true"
is-group-collapsable="true"
group-header-display-mode="Combined"
group-summary-display-mode="RowBottom"
column-moving-mode="Deferred"
column-moving-animation-mode="SlideOver"
column-moving-separator-width="2"
column-showing-animation-mode="slideFromRightAndFadeIn"
column-hiding-animation-mode="slideToRightAndFadeOut"
selection-mode="SingleRow"
corner-radius-top-left="0"
corner-radius-top-right="0">
</igc-data-grid>
</div>
);
}
}
For example, in this code I need igc-data-grid component will be bundled into separate file. Also igniteui-webcomponents-core, igniteui-webcomponents-grids modules needs to be in separate files to be used in, probably, other stencil components.

NuxtJS passing props

I have an issue so I have in NuxtJS (vuejs) with a default layout like that
<template>
<div id="app">
<Loader #animation:complete = 'loader'/>
<Header/>
<nuxt :title = 'title'/>
<Footer/>
<BgDecor/>
</div>
</template>
<script>
import { gsap } from "gsap/dist/gsap";
import Loader from "#/layouts/parts/Loader";
import Header from "#/layouts/parts/Header";
import Footer from "#/layouts/parts/Footer";
import BgDecor from "#/layouts/parts/BgDecor";
export default {
data() {
return {
loaderDone: null,
};
},
components: {
Loader,
Header,
Footer,
BgDecor,
},
head() {
return {
titleTemplate: "%s - Product Designer UI/UX Designer Strategist",
meta: [
{
hid: "description",
name: "description",
content:
"Artistic Director, Web / Motion Designer, FrontEnd Developer for over 10 years, specializing in visual communication and web design",
},
{
hid: "keywords",
name: "keywords",
content:
"Product Design, UI, UX, Designer, UI Designer, UX Designer, FrontEnd Developer",
},
],
};
},
methods: {
loader(e) {
console.log("yesssssss");
this.loaderDone = "yup";
},
},
computed: {
title() {
return this.loaderDone;
},
},
};
</script>
<style>
</style>
my issue is I have yup in Nuxt but not inside the page inside the component nuxt
yes I know it's complicated but I would like to when my loader is done get an $emit to my index page or other pages to know my loader is done
If I put the loader inside the index directly every time I come back to the page index I get again the loader I would like just to have it when I start my website that all
so in the dev tools I have the yup inside the nuxt parent of the index but if I go again with props it seems a lot of work for just getting info my loader is done I can start my other animation
If anyone has an idea thanks :)
have a great evening
You can do this with a vuex store.
Your Store could look like this
const store = new Vuex.Store({
state: {
loaderDone: false
},
mutations: {
setLoadingDone (state) {
state.loadingDone = true
}
}
})
In your loader you could map the mutation and in your component you can map the state.
To set the done flag you can do this in your loader to have the method setLoadingDone()
// loader.vue
import { mapMutations } from 'vuex}
export default {
// ...
methods: {
...mapMutations(['setLoadingDone '])
}
}
And your other componens can get this value like this
// other components
import { mapState } from 'vuex}
export default {
// ...
computed: {
...mapState(['loadingDone '])
}
}

React lightgallery.js integration without JQuery?

I've been searching for a proper guidance for integrating lightgallery.js library into my application, but after several hours I did not find any solutions. Since I'm using React, I don't want to mix it with JQuery.
I've stumbled across many similar questions like this one, but since all of them are using JQuery, I can't use their solutions.
Also, I've found react-lightgallery package (React wrapper for lightgallery.js), but it does not include video support yet.
In the lightgallery.js documentation, there is the installation guidance. After completing all of the steps, importing lightgallery.js and trying to print it (as suggested here by the library owner), empty object is being shown.
What would be the best solution for this? Are there any good alternatives?
Thanks!
I have handled it this way. May be it's not complete and the best practice, but it gives you a general view to how to handle it
import React, { PureComponent } from "react";
import Gallery from "lightgallery.js";
import "lightgallery.js/dist/css/lightgallery.min.css";
class _Gallery extends PureComponent {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
let self = this;
this.gallery = document.getElementById("lightgallery");
lightGallery(this.gallery, {
dynamic: true,
dynamicEl: [
{
src:
"1.jpg",
thumb: "1.jpg",
subHtml:
"<h4>Fading Light</h4><p>Classic view</p>"
},
{
src:
"2.jpg",
thumb: "2.jpg",
subHtml:
"<h4>Bowness Bay</h4><p>A beautiful Sunrise</p>"
},
{
src:
"3.jpg",
thumb: "3.jpg",
subHtml: "<h4>Coniston Calmness</h4><p>Beautiful morning</p>"
}
]
});
this.gallery.addEventListener("onCloseAfter", function(event) {
window.lgData[self.gallery.getAttribute("lg-uid")].destroy(true);
self.props.onCloseGallery();
});
}
render() {
return <div id="lightgallery" />;
}
}
export default _Gallery;
Here is a working example with cloudinary at Cloudinary LightGallery
The code for the Cloundinary LightGallery React Component using Styled Components and styled css grid is below.
The Code for the upload component is in my GitHub Repo at.
UploadWidget
/* eslint-disable indent */
import React, { Component, Fragment } from 'react'
import { LightgalleryProvider, LightgalleryItem } from 'react-lightgallery'
import axios from 'axios'
import styled from 'styled-components'
import { CloudinaryContext, Transformation, Image } from 'cloudinary-react'
import { Grid, Cell } from 'styled-css-grid'
import { media } from '../../utils/mediaQuery'
import 'lightgallery.js/dist/css/lightgallery.css'
import 'lg-autoplay.js'
const SectionTitle = styled.h3`
font-size: 1em;
margin: 0.67em 0;
${media.xs`
font-size: .85em;
`}
`
class Gallery extends Component {
constructor (props) {
super(props)
this.link = React.createRef()
this.state = {
gallery: [],
isOpen: false,
link: this.href,
}
}
componentDidMount () {
// Request for images tagged cats
axios.get('https://res.cloudinary.com/mansbooks/image/list/v1557911334/cats.json')
.then(res => {
console.log(res.data.resources)
this.setState({ gallery: res.data.resources })
})
}
onLink (event) {
this.setState({ link: this.href =
`https://res.cloudinary.com/mansbooks/image/upload/${data.public_id}.jpg` })
}
uploadWidget () {
let _this = this
cloudinary.openUploadWidget({ cloud_name: 'mansbooks', upload_preset: 'photos-
preset', tags: ['cats'], sources: ['local', 'url', 'camera', 'image_search',
'facebook', 'dropbox', 'instagram'], dropboxAppKey: 'Your API Key', googleApiKey:
'Your API Key' },
function (error, result) {
// Update gallery state with newly uploaded image
_this.setState({ gallery: _this.state.gallery.concat(result) })
})
}
render () {
return (
<div>
<Fragment>
<SectionTitle>Gallery by Cloudinary</SectionTitle>
<div>
<CloudinaryContext cloudName='mansbooks'>
<Grid columns='repeat(auto-fit,minmax(260px,1fr))' id='hash'>
<LightgalleryProvider>
{
this.state.gallery.map(data => {
return (
<Cell key={data.public_id}>
<LightgalleryItem group='group1' src={`https://res.cloudinary.com/mansbooks/image/upload/${data.public_id}.jpg`} data-sub-html={'data.public_id'}>
<Image publicId={data.public_id} onClick={() => this.setState({ isOpen: true })}>
<Transformation
crop='scale'
width='250'
height='170'
radius='6'
dpr='auto'
fetchFormat='auto'
responsive_placeholder='blank'
/>
</Image>
</LightgalleryItem>
</Cell>
)
})
}
</LightgalleryProvider>
</Grid>
</CloudinaryContext>
</div>
</Fragment>
</div>
)
}
}
export default Gallery

React JS ES6 with Froala Editor Configuration

I'm a newbie with ReactJS and I'm doing a project with ReactJS ES6. In my project, I use Froala Editor, it works perfectly. But now, I have a new feature requires Froala Editor adding input text, text area so Froala need more configuration. I don't know where to place configuration in ReactJS component. Here is my React component.
import FroalaEditor from 'react-froala-wysiwyg'
import FroalaEditorView from 'react-froala-wysiwyg/FroalaEditorView'
class RequestFormComponent extends Component {
constructor(props) {
super(props);
this.handleModelChange = this.handleModelChange.bind(this);
$.FroalaEditor.DefineIcon('insertInputField', {NAME: 'plus'});
$.FroalaEditor.RegisterCommand('insertInputField', {
title: 'Insert InputField',
focus: true,
undo: true,
refreshAfterCallback: true,
callback: function () {
this.html.insert(some input text);
}
});
this.state = this._getState();
}
_getState() {
return {
content: "Some text",
config: {
toolbarButtons: ['undo', 'redo', 'clearFormatting', 'selectAll', 'html', 'insertInputField']
}
}
}
handleModelChange(model) {
this.setState({content: model});
}
render() {
return (
<FroalaEditor
model={this.state.content}
onModelChange={this.handleModelChange}
config = {this.state.config}
/>
)
}
If I config like this, console will show error message "Cannot read property 'DefineIcon' of undefined"
I researched a lot but got nothing.
Please give me some advices to fix this issue.
I think you should import jquery:
import $ from 'jquery';
You have to import jQuery.
import $ from 'jquery';
Install jQuery by npm i jquery -s command.
And have to trigger the jQuery in componentDidMount method.
componentDidMount() {
$.FroalaEditor.DefineIcon('insertInputField', {NAME: 'plus'});
$.FroalaEditor.RegisterCommand('insertInputField', {
title: 'Insert InputField',
focus: true,
undo: true,
refreshAfterCallback: true,
callback: function () {
this.html.insert(some input text);
}
});
this.state = this._getState();
}

Categories