SFC styles not being applied with testing-library - vue.js

I'm adding unit tests to our vue codebase and finding difficult to test an element visibility.
I render the component as usual and as recommended in the examples of the testing-library documentation, but the styles are not being applied to the DOM debugging (using screen.debug). Any clue on this?
Example:
Inside the component
<template>
<span class="error-message>content here</span>
</template>
<style lang="scss" scoped>
.error-message {
display: none
}
In the (failing) test file:
render(Component);
expect(screen.getByText(/content here/i)).not.toBeVisible();
screen.debug();
The test fails because the element is visible. The debug prints the DOM as expected, except for the data-v-* classes that we usually see on browser inspect.

I ended up changing the way the assertion was being made.
As i understand, the styles being applied through the class error-message is not going to be seen by the jest-dom because it wont be inject to the DOM's header in the test environment. So what i did was to assert the class names in the element.

Related

How to build web component with styling library using vite and vue 3?

I am able to build vue web component and load it in other pages, but I can't find document how to correctly include a UI framework. It seems the web component is under shadowDOM and import css using style tag won't work.
(Add the CDN link in the template and style is applied)
Any hint on any framework, Vuetify or Ant Design or Tailwind CSS will be appreciated.
Similar question: Vuetify build as Web component style not showing
Using custom elements without Shadow DOM is trivial. Just add like the way you do traditionally. However, with Shadow DOM, things are tricky. Only inheritable CSS styles pass through the Shadow DOM. Everything else is blocked. No straight forward integration with existing design systems (Vuetify, Ant, etc.) is not directly possible if that library is only exposing global CSS.
If the design system or a component library is exposing styles i.e. css files for individual components, then you can that with some effort.
The best solution is to use constructable stylesheet. You can use a bundler like Webpack to load the stylesheet for individual component (if and only if it is provided) as a string and feed it to the stylesheet constructor method as illustrated here.
// Read SCSS file as a raw CSS text using Webpack/Rollup/Parcel
import styleText from './my-component.scss';
const sheet = new CSSStyleSheet();sheet.replaceSync(styleText);
// Use the sheet inside the web component constructor
shadowRoot.adoptedStyleSheets = [sheet];
However, Firefox and Safari are yet to implement it.
If you need a fallback, then there are ways that are not so clean. Approach is same. Import the CSS/SCSS as a string and using the template literal, add it to the element's inner style tag.
import styleText from 'ant/button.css';
class FancyComponent extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<!-- Styles are scoped -->
<style>
${styleText}
</style>
<div>
<p>Hello World</p>
</div>
`;
}
}
customElements.define('fancy-comp', FacyComponent);
This all relies on the assumption that ant/material/veutify is exposing styles as individual files instead of one global file.
Alternately, the browsers have started supporting the link tag inside the Shadow DOM. But again it is really useful if you have styles for individual components. Read more about that here.

Vue won't render anything if there are *any* errors

I'm learning Vue, and I notice that when other people view their webpages, it mostly displays alright even if they haven't finished the code. This is the behavior I'm used to from vanilla html/css/javascript.
But every time I have any problem with a component, e.g. undefined variable, it breaks the whole component and nothing gets displayed. For example, in a Single File Component if I have
<template>
<form>
<label>Write the title:</label>
<input v-model="title"></input>
/* Bunch of other stuff */
<form>
</template>
<script>
export default {
data() {
return {
/* I forget to write title here */
}
}
}
</script>
<style>
</style>
The whole template won't get displayed. Why is this happening and how can I fix it? It makes development much harder.
Btw I'm using Vue CLI 3 and used Vue UI to create the project, and "npm run serve" to view the site.

Vue.js showing content that should be hidden by a v-if tag when user refreshes page

I have a Vue.js app that loads content in the created() method. I use a v-if tag to hide all of my UI until that content is loaded and ready to go. It works fine on the initial load, but if a user were to hit refresh in Chrome then the app displays (flashes momentarily) content that would not otherwise be displayed (based on the data being loaded in created).
From what I understand using the v-if tag, with a flag from my vuex store that indicates when the load has completed, is the proper way to hide content until I am ready to display it.
How can I avoid having the content flash on the refresh?
Vue.JS has solved this using the v-cloak directive. (see docs)
You add the v-cloak directive to your application's root element:
<div id="app" v-cloak>
...
</div>
Then add this CSS rule to your application's stylesheet:
[v-cloak] {
display: none;
}
Everything within the app div will then be hidden until Vue has initialized.

Why doesn't the nested component render as a slottable custom element?

Are there limitations to compiling Svelte components as custom elements? For instance, are we able to nest components? And fill slots in those nested components?
I'm having trouble using a Svelte component as a custom element in my older Vue app.
I've got a Select and a Modal component in this simplified example: https://svelte.dev/repl/4d4ad853f8a14c6aa27f6baf33751eb8?version=3.6.4
I'm then compiling these with a standard-fare rollup.config.js:
export default {
input: "src/components.js",
output: [
// ...
{ file: "dist/index.min.js", format: "umd", name }
],
plugins: [
svelte({
dev: !production,
customElement: true,
// ...
}),
resolve(),
commonjs(),
!production && livereload("public"),
production && terser()
],
// ...
};
Then I go to use the custom elements. On click of the <conversational-select>, I get markup that looks like the following:
<conversational-select label="Domains" firstvaluelabel="All Domains">
<!-- shadow-root -->
<style>...</style>
<span class="select" >
<div class="select-value">Governance Board</div>
<div class="select-label" ></div>
</span>
<!-- The below div is the appropriate markup for Modal but the style isn't inlined and isn't slotted.
<!-- maybe because it didn't append as <sk-modal>? -->
<div ><slot></slot></div>
</conversational-select>
The "Modal" is sort-of rendering. But it doesn't fill the slot with span .chips. Doesn't inline the styles like the conversational-select does. Doesn't seem to attach its own event listeners. But does seem to create the fade transition thanks to Svelte's transition:fade directive.
I can reproduce this with a vanilla html file, so it's not Vue's fault.
Am I breaking some known rule with custom elements, butting up against the limitations of Svelte's custom element compilation, or just mistaken somewhere?
I was the author of the Svelte github issue that has been mentioned. I believe that I have a fix here. There were a few issues that existed:
slotted was never set
"nested" elements were not being added correctly
I expect the Svelte authors to make changes to my pull request, but if you want to use it, you can:
Clone my branch
Run npm && npm build && npm link
Go to your project and run npm link svelte

Non-scoped styling in components applied only once when switching routes

Vue.js documentation for Scoped CSS mentions that
You can include both scoped and non-scoped styles in the same component
I built the example application for vue-router and used two single file components instead of the string templates of the example - the rendering is as expected.
I then tried to apply both scoped and non-scoped styles in the components. In the first one I have
<style scoped>
div {
color: white;
background-color: blue;
}
</style>
<style>
body {
background-color: green;
}
</style>
and the second one
<style scoped>
div {
color: white;
background-color: red;
}
</style>
<style>
body {
background-color: yellow;
}
</style>
The idea is to have the whole body background switch when choosing a specific route.
The scoped styles are OK - they change depending on the route.
The non-scoped ones do not (screenshots are from Chrome Dev Tools):
on initial application load (non routed yet) the background is white (which is OK - this is the default one and there is no route for /).
when choosing a route, the style for the body is applied correctly (say, green from the first component)
when switching routes and loading the second component the background changes to the new color, it looks like from Chrome Dev Tools that the current style for background-color is overwritten. All the other components elements are correctly rendered (content and scoped styling)
further switches keep the same background (and again, other elements of the relevant component are rendered correctly). There are no changes in Chrome Dev Tools (the last view above is unchanged)
In other words, it looks like the style is stacked and previously overwritten properties are not updated Is this expected behaviour?
I opened a bug report for this and it ended up being expected behaviour. The summary from the report comments:
Thorsten Lünborg:
Yes, this is expected. Vue (or rather, webpack) does not insert and
remove these styles, as you seem to think. They are injected into the
head once the component renders, and never removed.
A common pattern is to extarct all CSS into a single .css file in
production, which would have the same result.
My summary in the context of the question:
initially (no route, no component rendered) nothing was injected
the first component is rendered on route switch, its style is injected
the second component is rendered on route switch, its style is injected and overwrites the previous style
further route switches do not inject anything as each component was already rendered once. The last style used therefore stays as the authoritative one.
I will therefore fallback on binding the body class to the current component's data