I have a native web component (no, not react component)...that I would like to render server side (ssr) in Deno.
Here is the web component:
import { api } from '/global.js';
class StatsBar extends HTMLElement {
constructor() {
super();
this.stats = {};
}
async getStats() {
const res = await fetch(`${api}/stats`);
const data = res.ok && await res.json();
this.stats = data;
}
async connectedCallback() {
await this.getStats();
this.innerHTML = `
<style>
.stats {
margin: 2rem 0;
display: block;
width: 100%;
text-align: center;
padding: 1rem;
}
</style>
<div class="stats">
Read ${this.stats.urls.count.toLocaleString()} articles on ${this.stats.tags.count.toLocaleString()} topics by ${this.stats.visits.count.toLocaleString()} visitors so far...
</div>
`;
}
}
customElements.define("stats-bar", StatsBar);
export default StatsBar;
It makes an api to get data, this all works fine client side in browsers, but then google can't crawl it becuase google still doesn't do well with javascript.
I want to see if its possible (With Deno and Oak) to render this component server side.
Related
I am trying to use Nuxt 3 together with Vuetify 3 in SSR mode. I face a problem using display's breakpoints. What is more, this functionality works with Nuxt 2 and Vuetify 2.
The code below shows only div element with red background instead of green, although the screen size is large. The reason is that the initial DOM rendering, which happens on the server side, assumes that the screen's size is small. The hydration on the client side somehow doesn't take into account, that the real size is large, although you can see in the browser's web inspector a log information result green.
<template>
<div>
<div :class="divClass">Reactivity</div>
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
import { useDisplay } from 'vuetify'
const counter = ref(1)
const { lgAndUp } = useDisplay()
const divClass = computed(() => {
const result = lgAndUp.value ? 'green' : 'red'
console.log('result', result)
return result
})
</script>
<style>
.green {
background-color: green;
}
.red {
background-color: red;
}
</style>
This seems like a bug, but maybe I've done some silly mistake here. Could you look at this and verify? Thanks in advance :)
The project sources can be found on GitHub
You could use ref property and watch the lgAndUp value to update it :
<template>
<div>
<div :class="divClass">Reactivity</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { useDisplay } from 'vuetify';
const { lgAndUp } = useDisplay();
const divClass = ref('');
watch(lgAndUp, (val) => {
console.log(val);
divClass.value = val ? 'green' : 'red';
},{immediate:true});
</script>
<style>
.green {
background-color: green;
}
.red {
background-color: red;
}
</style>
DEMO
This is my first attempt at writing unit tests for a Svelte app. The below example is based on the standard Svelte template with an additional element added. In this element I conditionally render some text with await block logic.
<script land="ts">
import { asyncthing } from "./asyncthing";
import { onMount } from "svelte";
export let name: string;
let isReady: Promise<boolean> = asyncthing().then((resolveTo) => {
console.log("asyncthing resolved");
return resolveTo;
});
</script>
<main>
<h1 data-testid="greeting">Hello {name}!</h1>
<p>
Visit the Svelte tutorial to learn
how to build Svelte apps.
</p>
<p data-testid="async">
{#await isReady}Awaiting...{:then value}Resolved to: {value}{/await}
</p>
</main>
<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
}
h1 {
color: #ff3e00;
text-transform: uppercase;
font-size: 4em;
font-weight: 100;
}
#media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
The function asyncthing returns a Promise<boolean> that resolves after 5 seconds with true.
export const asyncthing = async () => {
console.log("asyncthing() called");
return new Promise<boolean>((resolve) => {
setTimeout(() => {
console.log("timeout expired");
resolve(true);
}, 5000);
});
};
Opening the app in a browser works as expected, but the test below fails.
import App from "./App.svelte";
import { render } from "#testing-library/svelte";
import { tick } from "svelte";
import * as asyncthings from "./asyncthing";
test("messing with async", async () => {
jest.setTimeout(20000);
jest.useFakeTimers();
jest.spyOn(global, "setTimeout");
const asyncthingMock = jest.spyOn(asyncthings, "asyncthing");
const app = render(App, { name: "Everybody" });
expect(app.getByTestId("greeting")).toBeInTheDocument();
expect(app.getByTestId("greeting")).toHaveTextContent("Hello Everybody!");
expect(app.getByTestId("async")).toBeInTheDocument();
expect(app.getByTestId("async")).toHaveTextContent("Awaiting...");
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 5000);
jest.runAllTimers();
await tick();
expect(app.getByTestId("async")).toHaveTextContent("Resolved to: true");
});
The log statements are printed, so the promise does seem to resolve. However, according to Jest the text is not updated. I'm sure I must be doing something wrong, so please, somebody set me straight.
I am working on a project where we make use of Styled Components with NextJS for server side rendering. We use a private package which we include in multiple custom projects. This private package contains styling, but in some projects we want to override the styling with our custom styling as can be seen in the code below. It seems like it has something to do with the component selector ${H1} not being able to find the actual H1 component. Why is this happening?
Code in private package:
const baseHeading = css`
font-family: ${(props) => props.theme.fonts.sec};
display: block;
line-height: 1.3;
font-weight: bold;
`;
export const H1Styles = css`
font-size: ${(props) => props.theme.fonts.content.headings.h1.b1};
text-transform: uppercase;
#media screen and (min-width: ${size.min.b4}) {
font-size: ${(props) => props.theme.fonts.content.headings.h1.b4};
}
`;
export const H1 = styled.h1`
${baseHeading};
${H1Styles};
`;
Code in custom component:
import { H1 } from '#testproject/testproject-core-ui'
....
<section className={className}>
<FooterBrandingInner>
<H1>
<span>{params.LeftColumnTitleRow1}</span>
<span>{params.LeftColumnTitleRow2}</span>
</H1>
....
const FooterBrandingInner = styled.div`
${H1} {
font-size: 3.8rem;
text-transform: none;
line-height: 1.2;
}
....
Code in _document.tsx
static async getInitialProps(ctx: any) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) => (props: any) => sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
Code in .babelrc
[
"babel-plugin-styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": false
}
]
Expected Behavior
I would expect to see the H1 without 'text-transform: uppercase' when server side rendering.
Actual Behavior
When we visit a page with the custom styling which is defined in our custom class, we will not see it while server side rendering. We do see it when we are not server side rendering. We do sometimes see a flash of the custom styling and it seems that the wrong ClassNames are being used on the component.
I have a component called LargeDialog which encapsulates a StyledDialogContent (both of which are from the Dialog class of the Material UI library).
LargeDialog.jsx
...
const StyledDialogContent = styled(DialogContent)`
padding: 30px;
`;
class LargeDialog extends Component {
...
render(){
return (<StyledDialogContent> ... </StyledDialogContent>) // Some content within.
}
}
...
The styled components adds a padding: 30px to the DialogContent.
I would like to override this with padding: 0px if the LargeDialog modal is reused in another place.
However, the generated webpack CSS has a random identifier i.e. MuiDialogContentroot-0-3-439 FullDialogModal__StyledDialogContent-ogd6um-6 iMpISc and I'm not sure how to target this.
AnotherComponent.jsx
import LargeDialog from './LargeDialog'
...
const LargeDialogWrapper = styled(LargeDialog)`
// What do I put here to override StyledDialogContent with a random identifier?
`;
class AnotherComponent extends Component {
}
...
I tried exporting StyledDialogContent and targetting it as such:
import LargeDialog, {StyledDialogContent} from './LargeDialog'
...
const LargeDialogWrapper = styled(LargeDialog)`
${StyledDialogContent} {
padding: 0px;
}
`;
But that didn't work too.
Example:
https://codesandbox.io/embed/styled-components-d5pzv?fontsize=14&hidenavigation=1&theme=dark
You target it within the style like so:
const Box = styled.div`
background-color: black;
height: 100px;
`;
const Yellow = styled.div`
background-color: blue;
height: 200px;
${Box} {
background-color: yellow;
}
`;
const App = () => {
return (
<>
<Box />
<Yellow>
<Box />
</Yellow>
</>
);
};
Refer to the related docs section.
If it helps, you can check this example file (note the Heading style for example).
An edit after OP question update
In your example, you missing className if you want to enable styling for your components.
Also, you need WrapperDiv to be a direct child, this is how the CSS works, remember that you writing simple CSS just in javascript:
class LargeDialog extends Component {
render() {
return (
<WrapperDiv className={this.props.className}>
<div>{this.props.children}</div>
</WrapperDiv>
);
}
}
const WrapperLargeDialog = styled(LargeDialog)`
${WrapperDiv} {
background-color: blue;
}
`;
// LargeDialog should be red.
// WrapperLargeDialog should be blue.
class App extends Component {
render() {
return (
<div>
<LargeDialog />
<br />
<WrapperLargeDialog>
<WrapperDiv />
</WrapperLargeDialog>
</div>
);
}
}
I'm trying to add click event listener for DOM. All the scripts are bundled with webpack which compiles it to 1 large file.
This is my component:
<template>
<div class="formchat-window">
<a>test</a>
<div class="title" v-on:click.self="toggle">NevĂte si rady? Zeptejte se!</div>
<wp-formchat-screen v-if="windowActive"></wp-formchat-screen>
</div>
</template>
<script>
export default {
mounted () {
console.log('mounted');
},
methods: {
toggle(event) {
alert('click on toggle');
this.windowActive = !this.windowActive;
}
},
data() {
return {
windowActive: false,
};
}
}
</script>
<style lang="scss">
$color: red;
.formchat-window {
position: fixed;
right: 10%;
bottom: 0;
width: 300px;
z-index: 9999;
.title {
background: $color;
}
}
</style>
This is my main JS file:
import Vue from 'vue'
import FormChat from './FormChat'
import FormchatAnswer from './components/FormchatAnswer'
import FormchatEntry from './components/FormchatEntry'
import FormchatScreen from './components/FormchatScreen'
import FormchatWindow from './components/FormchatWindow'
window.Vue = Vue;
console.log('test');
Vue.component('WpFormchatAnswer', FormchatAnswer);
Vue.component('WpFormchatEntry', FormchatEntry);
Vue.component('WpFormchatScreen', FormchatScreen);
Vue.component('WpFormchatWindow', FormchatWindow);
const app = new Vue({
el: '#wp-formchat-vue-root',
//render: h => h(FormChat)
});
However I did everything and compiler works perfectly, I cant register the click, so the method won't trigger.
Can anyone please help me?
I have finally figured it out. In the console, there was a warning about component name and main filename. I have renamed main file and the Vue started to work again.
Beware of ambigious naming :)