How to make Vue3 test utils work with teleport - vuejs3

I have a component that uses teleport to , the test html doesn't seem to be working as expected. I can't find any documentation on this particular use. Here's my test:
describe('MetaHead', () => {
it('dynamic metadata tags contain custom text', () => {
let title = 'My Page';
let description = 'Some description about my page';
// This component uses Vue3's teleport to tag <head>
// we must modify wrapper to contain such tag
document.body.innerHTML = `
<head>
<div id="app"></div>
</head>
`
const wrapper = mount(MetaHead, {
attachTo: document.getElementById('app'),
props: {
title,
description
},
global:{
mocks: {
$route:{fullPath: 'full/path'}
}
}
})
expect(wrapper.html()).toContain(title)
expect(wrapper.html()).toContain(description)
})
})
and the minimal component looks like this:
<template>
<teleport to="head">
<title>{{title}}</title>
<meta property="og:site_name" :content="title">
<meta name="description" :content="description">
</teleport>
</template>
Am I missing something?

the problem here is wrapper.html() only returns HTML in your component - since you are teleporting outside your component, that markup won't show up when you call wrapper.html().
You have a few options. One would be making an assertion against document.body.outerHTML. Another would be using a neat trick with findComponent, I wrote about it here and posted a video about it here.
Another thing you could try that I just thought of (but have not tested) would be:
mount({
template: `
<div id="app" />
<MetaHead />
`,
components: { MetaHead }
})
I don't know if that will work, but worth a try.

Related

Is there a way to cancel nextjs 13 layout?

I have the following pages: /, /accounts, /signin
I want to use a dashboard layout for the first 2 and a normal layout for the signin page. But i have no idea how i can achieve this. If i add the dashboard layout to the layout.tsx file i see it on the signin page and i can't override it there. If i add the layout to the page.tsx file, it kinda defeats the whole purpose of those layouts?
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head />
<body className="bg-gray-50">{children}</body>
</html>
);
}
vs
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head />
<body className="bg-gray-50">
<LayoutComponent />
<div className="p-4 mt-16 sm:ml-64">{children}</div>
</body>
</html>
);
}
You could leave the /singin page in the main directory and create an additional directory for dashboard pages, e.g. app/(dashboard), putting dashboard in parenthesis means that it won't affect the URL, see the docs.
The structure would look like:
- app
- layout.js <- your main layout
- singin
- page.js
- (dashboard)
- page.js
- layout.js <- your dashboard layout
- accounts
- page.js

How to export script and execute it with next/script

Salutations!
I am working on a Tailwind-css-Next.js-Typescript project trying to implement a theme toggler. It works fine except for the on-load flicker.
In the past I implemented a solution similar to https://github.com/vercel/next.js/discussions/12533 but now that only next/script is accepted, when using Typescript you can't export a script without export which paradoxically returns an Uncaught SyntaxError: Unexpected token 'export' error.Also scripts like this won't execute anyway if you add them in the Head instead of inside the body. Which in turn is a problem because tailwind wants the script in the head.
This is my current script (note that I must use export{} and create a module otherwise the Typescript linter doesn't let me save the file):
var theme = localStorage.getItem("dark-theme") || "light";
if (theme === "dark") {
document.documentElement.classList.add("dark");
}
export {};
And I try to use it like so:
import "./globals.css";
import Script from "next/script";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>
<Script
type="module"
strategy="beforeInteractive"
src="themeScript.tsx"
/>
{children}
</body>
</html>
);
}
But this causes a Uncaught SyntaxError: Unexpected token 'export' (at themeScript.tsx:1:1)
I have also tried adding the following both in the Head and in the body. It doesn't return an error but doesn't produce a result.
<Script id="find-dark-mode">{localStorage.getItem("dark-theme") === true ? document.documentElement.classList.add('dark') : document.documentElement.classList.remove('dark')}</Script>
This question is similar to Next 13 & Tailwind Darkmode Flickering but I am asking about next 13 scripts in general.
I appreciate your time in advance.

Botframework with WordPress

I want to implement BotFramework in a WordPress but in any way or form, it's not working properly.
I used different scripts but got to the same wrong result.
one:
<script>
(function () {
var div = document.createElement("div");
document.getElementsByTagName('body')[0].appendChild(div);
div.outerHTML = "<div id='botDiv' style='height: 38px; position: fixed;
bottom: 0; z-index: 1000; background-color: red'>
<div id='botTitleBar' style='height: 38px; width: 400px;
position:fixed; cursor: pointer;'></div>
[advanced_iframe src="https://webchat.botframework.com/embed/..."
width="100%" height="600"]</div>";
document.querySelector('body').addEventListener('click', function (e) {
e.target.matches = e.target.matches || e.target.msMatchesSelector;
if (e.target.matches('#botTitleBar')) {
var botDiv = document.querySelector('#botDiv');
botDiv.style.height = botDiv.style.height == '600px' ? '38px' : '600px';
};
});
}());
</script>
it's giving me the banner but not opening the chat when pressed.
in other case the script:
<!DOCTYPE html>
<html>
<body>
<div id="webchat" role="main"></div>
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<script>
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token: 'key' }),
userID: 'YOUR_USER_ID',
username: 'Web Chat User',
locale: 'en-US',
botAvatarInitials: 'WC',
userAvatarInitials: 'WW'
}, document.getElementById('webchat'));
</script>
</body>
</html>
but in this case, it's doing nothing.
help, please :(
I don't know how your environment is structured, so hopefully, this translates but I was able to accomplish this. I'm running this locally having spun up a WP site on a WAMP server.
First, I generate a token by making an API call to
https://directline.botframework.com/v3/directline/tokens/generate.
If you're already generating a token, then skip to the next section. If not, you can reference this code, found here (if of interest).
On WP, I am using a plugin called 'WP Coder' This allows you to enter in the necessary components while letting the plugin 'make it work' in the page. I tried hand-coding it in, but the WP page wasn't playing nice and this plugin was.
Once the plugin is installed, put this in the 'HTML code' section:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>WebChat</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="webchat" role="main"></div>
</body>
</html>
Followed by this in the 'CSS code' section:
html,
body {
height: 100%
}
body {
margin: 0
}
#webchat,
#webchat>* {
height: 500px;
width: 100%;
}
Btw, if you set the height to 100% for '#webchat' the chat will continuously scroll down the page, as entries are made, forcing the user to have to 'scroll after it'. Outside of that, adjust it as you will.
Under 'JS Code', add the following. Please note, that I'm generating a token locally. You will need to update this to match your method of token generation:
( async function () {
const res = await fetch( 'http://localhost:3979/directline/token', { method: 'POST' } );
const { token } = await res.json();
window.WebChat.renderWebChat( {
directLine: window.WebChat.createDirectLine( { token } )
}, document.getElementById( 'webchat' ) );
} )();
Next, under 'Include files' enter the two following JS files as URLs (individually):
https://unpkg.com/markdown-it/dist/markdown-it.min.js
https://cdn.botframework.com/botframework-webchat/master/webchat.js
Lastly, take the Publish 'shortcode' (mine looks like this [WP-Coder id="1"]) and place it on your page. This is found in the WP Coder plugin.
At this point, it should work for you. If not, I would look closely at how you are generating and passing the token.
Hope of help!

Universal rendering creates a delay between DOMContentLoaded event to Load event

I'm very excited about styled components and would love to use it if it wasn't for this...
I've prepared two example projects using next.js universal rendering library.
The first example is using styled-components as a solution, and the second one is using their default solution for css which is styled-jsx.
Both examples include exactly the same code with a minimum level of complexity.
As you will soon see for yourself - in the styled-components example there is a disturbing delay between DOMContentLoaded event and Load event inwhich the user actually sees the un-styled html markup, while in the second example using styled-jsx this is not the case.
Both demos are hosted online using Zeit now:
1 - https://01-styled-components-sqprkdqeft.now.sh
2 - https://02-styled-jsx-nhrynpsdox.now.sh
Source available on github:
1 - https://github.com/Ajar-Ajar/next-demo--styled-components
2 - https://github.com/Ajar-Ajar/next-demo--styled-jsx
I would very much appreciate any insights regarding why does it happen in one and not the other,
and of course any way to amend this behavior as I would love to use styled-components for its many features and advantages.
Thank you
Ajar
:)
What is missing here is the style injection on the server. Basically, when you write styles in JavaScript you have to get the generated styles on the server and inject them as a style tag into the generated HTML.
The built-in solution for Next does this automatically for you, with styled-components you have to do a tiny bit of manual work and add a pages/_document.js file that looks like this:
import Document, { Head, Main, NextScript } from 'next/document'
import { styleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps ({ renderPage }) {
const page = renderPage()
const styles = (
<style dangerouslySetInnerHTML={{ __html: styleSheet.rules().map(rule => rule.cssText).join('\n') }} />
)
return { ...page, styles }
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Notice how we inject a style tag with the styles from styled-components. That's all there is to it, now that flash of unstyled content is gone! 🎉 (this is taken from the official example)
Note: With v2 of styled-components (coming soon, you can get it right now with `npm i --save styled-components#next) there'll be an official API for SSR so it'll look more like this:
import Document, { Head, Main, NextScript } from 'next/document'
import styleSheet from 'styled-components/lib/models/StyleSheet'
export default class MyDocument extends Document {
static async getInitialProps ({ renderPage }) {
const page = renderPage()
const styles = (
<style dangerouslySetInnerHTML={{ __html: styleSheet.getCSS() }} />
)
return { ...page, styles }
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Hope that helps!
Here is the recommended way for using styled-components with next to avoid the issue:
https://github.com/vercel/next.js/blob/master/examples/with-styled-components/pages/_document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
}
} finally {
sheet.seal()
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}

Enyo error: "Uncaught referenceError: App is not defined"

I am getting the error: "Uncaught referenceError: App is not defined" in my JS console when loading this Enyo app on my localhost. I am brand new to Enyo so I am still trying to learn the concepts of kinds and components.
app.js (in source folder):
enyo.kind({
name: "App",
kind: "FittableRows",
classes: "enyo-fit enyo-unselectable",
components: [
{
kind: "onyx.Toolbar",
layoutKind:"FittableColumnsLayout",
components: [
{
kind:onyx.Button,
style:"width:80px;background:green;",
ontap:"handleBtnBack",
content:"Back"
},
{
content:"Header",
style:"text-align:center;",
fit:true
},
{
kind:onyx.Button,
style:"width:80px;background:red;",
ontap:"handleBtnNext",
content:"Next"
}
]
},
{
kind: "Scroller",
horizontal:"hidden",
touch:true,
fit:true,
thumb:true,
components:[
{
tag:"h1",
//This is how we insert css class.
classes:"padding15px",
content:"This is content area...Hello World!!!"
}
]
},
{
kind: "onyx.Toolbar",
// The footer
layoutKind:"FittableColumnsLayout",
components:[
{
kind:"onyx.Button",
content:"Go Next Page",
ontap:"handleBtnNextPage",
fit:true
}
]
}
],
create: function(){
this.inherited(arguments);
console.log("App is created in memory");
},
rendered : function(){
this.inherited(arguments);
console.log("App is created in rendered into DOM");
},
handleBtnNextPage : function(inSender,inEvent){
new Page2().renderInto(document.body);
},
handleBtnNext: function(inSender,inEvent){
new Page2().renderInto(document.body);
},
handleBtnBack: function(inSender,inEvent){
//For each enyo event handler comes with inSender, the control that sends the event and the inEvent the actual event itself.
alert("Back Button");
}
});
package.js (in source folder):
enyo.depends(
// Layout library
"$lib/layout",
// Onyx UI library
"$lib/onyx", // To theme Onyx using Theme.less, change this line to $lib/onyx/source,
//"Theme.less", // uncomment this line, and follow the steps described in Theme.less
// CSS/LESS style files
"../assets/css/app.css",
// Include our default entry point
"App.js",
"Page2.js"
);
index.html (in root folder):
<!--My Copy-->
<!DOCTYPE html>
<html>
<head>
<title>IsGoodStuff.com Tutorial #2</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="shortcut icon" href="assets/favicon.ico"/>
<script src="enyo/enyo.js" type="text/javascript"></script>
<!-- -->
<script src="package.js" type="text/javascript"> </script>
</head>
<body>
<script type="text/javascript">
new App().renderInto(document.body);
</script>
</body>
</html>
If your index.html is in your root folder, but the main package.js is in the source folder, it's probably your script tag that loads package.js. Try:
<script src="source/package.js" type="text/javascript"> </script>
You haven't supplied Page2 but it appears the code would work as-is.
Here's a fiddle showing the working page: http://jsfiddle.net/kgxvg7Lw/1/
Some thoughts:
1) Are you using a case-sensitive file system? You show app.js but your package.js has App.js (capitalized).
2) Are you certain there are no parse errors in the console?
Now, that said... You probably don't want to reload a new app for every 'page' switch. Usually, you would use something like Panels to allow the app to control the content that appears on the screen and just navigate among the panels as needed.

Resources