I'm building a component library of just HTML snippet and corresponding js/css and I'm using Docusaurus to document those compoents. I have a document page for each component. On the document page there is an example of each component. I'd like to make the components functional (click events, keyboard nav, etc.) so I have attached the component javascript via a plugin:
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin-component-assets',
injectHtmlTags() {
return {
headTags: [
{
tagName: 'link',
attributes: {
rel: 'stylesheet',
href: 'https://example.com/css/component.min.css',
},
},
],
postBodyTags: [
{
tagName: 'script',
attributes: {
src: 'https://example.com/js/component.min.js',
},
},
],
};
},
};
};
In my docusaurus.config.js I've added my plugin:
...
plugins: [
'docusaurus-plugin-sass',
path.resolve(__dirname, 'src/plugins/docusaurus-plugin-component-assets.js')
],
...
This successfully adds the stylesheet and javascript in the correct locations. However the javascript never executes. It appears that my component javascript fires before the documentation app loads.
What am I missing? What is the correct way to add external javascript to documentation pages?
EDIT: I'm using "#docusaurus/core": "2.0.0-beta.0",
I struggled with this too (on Docusaurus v2). Eventually I understood what the Client Modules documentation was saying and did the following, which worked for me both for the initial page load and the page loaded after a navigation event. (Since this is a single-page app, it's not a full page load when you're just navigating around the documentation, and I had to handle that case separately.)
Create a new file at plugins/my-script.js (or whatever you want to call it).
Add clientModules: [require.resolve('./plugins/my-script')], to your config in docusaurus.config.js.
Insert code like this into your new file:
import ExecutionEnvironment from '#docusaurus/ExecutionEnvironment';
const doYourCustomStuff = () => {
// your JS code goes here
}
export function onRouteDidUpdate({location, previousLocation}) {
// Don't execute if we are still on the same page; the lifecycle may be fired
// because the hash changes (e.g. when navigating between headings)
if (location.pathname === previousLocation?.pathname) return;
doYourCustomStuff();
}
if (ExecutionEnvironment.canUseDOM) {
// We also need to setCodeRevealTriggers when the page first loads; otherwise,
// after reloading the page, these triggers will not be set until the user
// navigates somewhere.
window.addEventListener('load', () => {
setTimeout(doYourCustomStuff, 1000);
});
}
Then put your JS code inside the given function.
Caveat: your effects will still be broken when hot-loading changes from a yarn start dev environment. The not-too-painful workaround is to manually reload in such cases.
You can embed your components in Live Code block Docusaurus V2.
You will need to install the package using npm or yarn.
npm install --save #docusaurus/theme-live-codeblock
module.exports = {
plugins: ['#docusaurus/theme-live-codeblock'],
themeConfig: {
liveCodeBlock: {
/**
* The position of the live playground, above or under the editor
* Possible values: "top" | "bottom"
*/
playgroundPosition: 'bottom',
},
},
};
You can find detailed information about this process by using the link below.
https://docusaurus.io/docs/markdown-features/code-blocks#interactive-code-editor
It should be noted that this only works with react components.
Related
I am super new to Nuxt, and I am currently trying to move over a vue application that generates gifs ffmpeg.wasm to use Nuxt.js. However, whenever I visit the page the server crashes with the following error:
[fferr] requested a shared WebAssembly.Memory but the returned buffer is not a SharedArrayBuffer, indicating that while the browser has SharedArrayBuffer it does not have WebAssembly threads support - you may need to set a flag 18:36:38
(on node you may need: --experimental-wasm-threads --experimental-wasm-bulk-memory and also use a recent version)
I know it says to add the flags to node, as does the ffmpeg.wasm docs, but how do I do that via Nuxt? Or can I even do that? It is using the default dev server that comes with Nuxt, and I will be able to solve this when it's built and hosted but I need to have it locally as well.
Here is the component I am using in my barebones Vue app (stripped back but still causes an error). I am using node v14.17.6 and I'm using this library github.com/ffmpegwasm/ffmpeg.wasm/blob/master/README.md
<template>
<div class="home">
<h1>FFMPEG test</h1>
</div>
</template>
<script>
import { createFFmpeg } from '#ffmpeg/ffmpeg'
export default {
name: 'Home',
data: function () {
return {
ffmpeg: null,
}
},
created() {
this.ffmpeg = createFFmpeg({
log: true,
})
// Initialise loadFFmpeg
this.loadFFmpeg()
},
methods: {
async loadFFmpeg() {
await this.ffmpeg.load()
},
},
}
</script>
Creating the instance into a mounted() fixed the issue.
This is probably due to the fact that ffmpeg needed the Vue instance to be already mounted in the DOM (in the way it works).
created() is usually used for AJAX calls or things not so related to the Vue instance, and it being shown with the Composition API in their example gave me the idea of trying the mounted() hook.
I'm not sure if this is actually an issue with the plugin and my configurations, or if it is just standard behaviour.
I'm using react-i18next with i18next-http-backend to load translation files from the public folder. The lazy-loading is a nice bonus but I'm actually using the backend plugin because the files are managed externally (by non-programmers) and I can't know upfront which files exist. I'm not working server-side here so I can't read from the file system directly.
Problem: I have a collapsible section whose content is only rendered on expanding the section. When that content requires a translation file that was not previously loaded, the fetching of the file seems to trigger a page reload: it flickers and scrolls up.
It seems strange to me that the page is flickering because of fetching a file. I suppose that i18next is updating because it's loading a new namespace and that causes the flickering. Does that make sense? If so, is there a way to tell the http-backend all the namespaces (i.e, all the filenames in /public/locales), still keeping the lazy-loading? Am I missing something in my configuration?
Here's my configuration:
import en from './en/translation.json';
import de from './de/translation.json';
const localResources = { ...de, ...en };
const customLocalBackend = {
type: 'backend',
init: function (services, backendOptions, i18nextOptions) {
/* use services and options */
},
read: function (language, namespace, callback) {
callback(null, localResources[language][namespace]);
},
};
export const i18n = i18next
.use(initReactI18next)
.use(ChainedBackend)
.use(LanguageDetector)
.init({
lng: 'de',
fallbackLng: 'de',
supportedLngs: ['de','en'],
load: 'languageOnly',
debug: true,
backend: {
backends: [
HttpBackend, // load resources from /public folder
customLocalBackend, // load local resources
],
backendOptions: [
{
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
{},
],
},
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
keySeparator: false,
});
Thanks for any help!
The problem was caused by a <React.Suspense fallback={<div />}> that was wrapping the full react app. When a new translation was injected at run time, the fallback div was rendered, causing the "flickering".
I removed the wrapper and set react: { useSuspense: false } in the i18n configuration and everything is working as expected now.
I have a blog run on Gatsby, and every time I push and deploy new blog posts I need to do a refresh on my page to see the new data.
I tried following the suggestions from this post and adding an onServiceWorkerUpdate function but that doesn't seem to have done anything.
Anyone have workarounds to this issue, and if so will there be a way to test locally? Changes already automatically update when I test in gatsby develop mode.
This is the entirety of my gatsby-browser.js file
export const onServiceWorkerUpdateReady = () => window.location.reload();
You need to install gatsby-plugin-offline first. Leaving your gatsby-config.js with something similar to:
{
plugins: [
{
resolve: `gatsby-plugin-manifest`,
options: {
...
}
},
'gatsby-plugin-offline'
]
}
Note: plugin's order matters in this case.
The plugin will register a service worker and will load it into the client.
Then, in your gatsby-browser.js file you can simply add:
export const onServiceWorkerUpdateReady = () => {
const answer = window.confirm(
`This application has been updated. ` +
`Reload to display the latest version?`
)
if (answer === true) window.location.reload()
}
Additionally, you may want to add the following hack (very common across the repositories):
export const onRouteUpdate = () => {
navigator.serviceWorker.register('/sw.js').then((reg) => {
reg.update();
});
};
Basically, it forces the registration of the service-worker across the site upon the onServiceWorkerUpdateReady refresh.
Check this interesting thread for further caveats and workarounds for some specific scenarios: https://github.com/gatsbyjs/gatsby/issues/9087#issuecomment-774680408
It will not work using Link provided by gatsby
import { Link as GatsbyLink } from 'gatsby';
<GatsbyLink to={to} {...props} />
It works using traditional a tag:
<a href={props.to} target="_blank">
{props.children}
</a>
Is there any way to configure gatsby to inject a style file using a link tag?
currently, I have a global style (style.less) which I import it using a Layout Component. It's ok but It injects all CSS content into each page and bumps the page size.
I want to configure gatsby to load this style file using a link tag instead of injecting directly into DOM. webpack has an option for this purpose but I couldn't find anything similar for Gatsby.
Try:
exports.onCreateWebpackConfig = ({ actions, loaders, getConfig }) => {
const config = getConfig();
config.module.rules = [
...config.module.rules.filter(rule => String(rule.test) !== String(/\.jsx?$/)),
{
test: /\.link\.css$/i,
use: [
{ loader: `style-loader`, options: { injectType: `linkTag` } },
{ loader: `file-loader` },
],
},
];
actions.replaceWebpackConfig(config);
};
Gatsby allows you to customize the webpack's configuration by exposing some APIs (such as onCreateWebpackConfig or setWebpackConfig) functions.
The code is quite self-explanatory if you take into account the style-loader from webpack. Basically, you are setting some custom loaders for all files that match the regular expression, and finally, you override the default configuration with actions.replaceWebpackConfig(config).
For further details check Adding Custom webpack configuration docs.
In addition, regarding your original issue, you don't need to add your global styles in your Layout component since it will cause what you said, it will bump the page size. With Gatsby, you can use gatsby-browser.js API to add global styles like (in your gatsby-browser.js file):
import './src/your/path/to/global/style.less';
Check the link you've provided (Standard Styling with Global CSS file).
I try to use an external script (https://libs.crefopay.de/3.0/secure-fields.js) which is not vue based
I added the script via -tags into index.html
But when I try to intsanciate an object, like in the excample of the script publisher.
let secureFieldsClientInstance =
new SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
Vue says "'SecureFieldsClient' is not defined"
If I use this.
let secureFieldsClientInstance =
new this.SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
secureFieldsClientInstance.registerPayment()
Vue says: Error in v-on handler: "TypeError: this.SecureFieldsClient is not a constructor"
My Code:
methods: {
startPayment () {
this.state = null
if (!this.selected) {
this.state = false
this.msg = 'Bitte Zahlungsweise auswählen.'
} else {
localStorage.payment = this.selected
let configuration = {
url: 'https://sandbox.crefopay.de/secureFields/',
placeholders: {
}
}
let secureFieldsClientInstance =
new SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
secureFieldsClientInstance.registerPayment()
// this.$router.replace({ name: 'payment' })
}
}
}
Where is my mistake?
EDIT:
Updated the hole question
Here is a minimal Vue app for the context your provided, which works:
https://codepen.io/krukid/pen/voxqPj
Without additional details it's hard to say what your specific problem is, but most probably the library gets loaded after your method executes, so window.SecureFieldsClient is expectedly not yet defined. Or, there is some runtime error that crashes your script and prevents your method from executing. There could be some other more exotic issues, but lacking a broader context I can only speculate.
To ensure your library loads before running any code from it, you should attach an onload listener to your external script:
mounted () {
let crefPayApi = document.createElement('script')
crefPayApi.onload = () => this.startPayment()
crefPayApi.setAttribute('src', 'https://libs.crefopay.de/3.0/secure-fields.js')
document.head.appendChild(crefPayApi)
},
I found the solution.
the import was never the problem.
I had just to ignore VUEs/eslints complaining about the missing "this" via // eslint-disable-next-line and it works.
So external fuctions/opbjects should be called without "this" it seems.
let secureFieldsClientInstance =
new SecureFieldsClient('xxxxx',
this.custNo,
this.paymentRegisteredCallback,
this.initializationCompleteCallback,
configuration)
You could download the script and then use the import directive to load the script via webpack. You probably have something like import Vue from 'vue'; in your project. This just imports vue from your node modules.
It's the exact same thing for other external scripts, just use a relative path. When using Vue-CLI, you can do import i18n from './i18n';, where the src folder would contain a i18n.js
If you really want to use a CDN, you can add it like you normally would and then add it to the externals: https://webpack.js.org/configuration/externals/#externals to make it accessible from within webpack