Comments (4)
Here is an up-to-date POC implementation based on the latest release of storybook-django, which includes framework-agnostic APIs:
<template>
<component ref="elt" :is="computedTag"></component>
</template>
<script lang="ts">
import type { PropType } from 'vue'
import Vue from 'vue'
import { renderPattern, simulateLoading } from 'storybook-django';
const getTemplateName = (template?: string, filename?: string): string =>
template ||
filename?.replace(/.+\/templates\//, '').replace(/\.stories\..+$/, '.html') ||
'template-not-found';
export default Vue.extend({
name: "TemplatePattern",
props: {
element: {
type: String,
required: true,
},
template: {
type: String,
required: false,
},
filename: {
type: String,
required: false,
},
context: {
type: Object,
required: true,
},
tags: {
type: Object,
required: true,
},
},
data(): {
error: Error | null;
} {
return {
error: null,
};
},
computed: {
computedTag(): string {
return this.element || 'div'
},
},
mounted() {
// TODO Should be called whenever the component re-renders, not just on mount.
this.getRenderedPattern();
},
methods: {
async getRenderedPattern(): Promise<void> {
const templateName = getTemplateName(this.template, this.filename);
renderPattern(window.PATTERN_LIBRARY_API, template_name, this.context, this.tags)
.catch((err) => simulateLoading(this.$refs.elt, err))
.then(res => res.text())
.then((html) => simulateLoading(this.$refs.elt, html));
},
},
});
</script>
I believe it should be possible to add support for Vue directly in storybook-django – will give this a go in a future release.
from storybook-django.
Thank you so much for taking the time to sketch this out for me @thibaudcolas, I’ll get stuck in!
from storybook-django.
Hey @laurencedorman, not very much.
The current implementation technically is coupled, but the current implementation is very hacky, and could easily be refactored to be framework-agnostic. Here is where most of the magic happens: https://github.com/torchbox/storybook-django/blob/main/src/TemplatePattern.js#L30-L129
This is technically a React component, but we only use it to render a <div>
, store a bit of state – and the bulk of the logic is built with vanilla JS executed imperatively in the relevant React lifecycle phases.
Here is a PoC of a Vue version. Note I don’t do Vue very often myself so this likely isn’t correct. In particular, we’d want the API call to be made on every render of the component, not just on mount.
<template>
<component ref="elt" :is="computedTag"></component>
</template>
<script lang="ts">
import type { PropType } from 'vue'
import Vue from 'vue'
/**
* Inserts HTML into an element, executing embedded script tags.
* @param {Element} element
* @param {string} html
*/
const insertHTMLWithScripts = (element, html) => {
element.innerHTML = html;
Array.from(element.querySelectorAll('script')).forEach((script) => {
const newScript = document.createElement('script');
Array.from(script.attributes).forEach((attr) =>
newScript.setAttribute(attr.name, attr.value),
);
newScript.appendChild(document.createTextNode(script.innerHTML));
script.parentNode.replaceChild(newScript, script);
});
};
export default Vue.extend({
name: "TemplatePattern",
props: {
element: {
type: String,
required: true,
},
apiPath: {
type: String,
required: true,
},
template: {
type: String,
required: true,
},
context: {
type: Object,
required: true,
},
tags: {
type: Object,
required: true,
},
},
data(): {
error: Error | null;
} {
return {
error: null,
};
},
computed: {
computedTag(): string {
return this.element || 'div'
},
},
mounted() {
// TODO Should be called whenever the component re-renders, not just on mount.
this.getRenderedPattern();
},
methods: {
async getRenderedPattern(): Promise<void> {
const url = this.apiPath || window.PATTERN_LIBRARY_API;
let template_name = window.PATTERN_LIBRARY_TEMPLATE_DIR
? this.template
.replace(window.PATTERN_LIBRARY_TEMPLATE_DIR, ";;;")
.replace(/^.*;;;/, "")
: this.template;
template_name = template_name.replace(".stories.js", ".html");
window
.fetch(url, {
method: "POST",
mode: "same-origin",
cache: "no-cache",
credentials: "omit",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
template_name,
config: {
context,
tags,
},
}),
})
.catch(() => {
if (this.$refs.elt) {
insertHTMLWithScripts(this.$refs.elt, "Network error");
}
})
.then((res) => {
if (res.ok) {
setError(null);
return res.text();
}
return res.text().then((serverError) => {
let errName = serverError.split("\n")[0];
let stack = serverError;
if (serverError.includes("TemplateSyntaxError")) {
try {
let templateError;
templateError = serverError.split("Template error:")[1];
templateError = templateError.split("Traceback:")[0];
templateError = templateError
.split("\n")
.filter((l) => l.startsWith(" "))
.map((l) => l.replace(/^\s\s\s/, ""))
.join("\n");
const errCleanup = document.createElement("div");
errCleanup.innerHTML = templateError;
stack = errCleanup.innerText;
let location = serverError
.split("\n")
.find((l) => l.startsWith("In template"));
errName = `TemplateSyntaxError ${location ? location : ""}`;
} catch {}
}
const error = new Error(errName);
error.stack = stack;
setError(error);
return "Server error";
});
})
.then((html) => {
if (this.$refs.elt) {
insertHTMLWithScripts(this.$refs.elt, html);
window.document.dispatchEvent(
new Event("DOMContentLoaded", {
bubbles: true,
cancelable: true,
})
);
}
});
},
},
});
</script>
from storybook-django.
I have released a new version of the project, with framework-agnostic APIs (documented in the README), and an optional React component. It seems possible to also add support for Vue 3 in a similar way. Here is the reference React implementation:
https://github.com/torchbox/storybook-django/blob/main/src/react.js
The only thing that is still coupled with the React implementation is data-
attributes that are only needed to support automated tests. They’d be pretty straightforward to re-implement in a Vue version.
from storybook-django.
Related Issues (9)
- Upgrade to Storybook v7
- Missing contribution guidelines and how-to for running the project locally
- This project no longer runs correctly
- can i use this to build my own server side pattern library aimed at tailwindcss? HOT 2
- Would be good if a bit more information is provided for getting started HOT 1
- What does it mean to copy `demo/storybook` into your project? HOT 1
- Am I right to say if I don't use any client side frameworks like React, Vue, etc I should just stick to pattern-library? HOT 3
- Document tag overrides
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from storybook-django.