loading...

Discussion on: Lets Build Web Components! Part 3: Vanilla Components

jimisdrpc profile image
jimisdrpc

Benny, I have been studding open-wc but I am wondering if they are promoving really vanilla webcomponents or they are somehow promoving lit-html which I understand it is kind of new flavour of polymer from Google. See their vanilla example importing lit-html from github.com/open-wc/example-vanilla.... I am not saying is bad or worst, I am just interested to really learn webcomponent deepelly and avoid frameworks at least for now. What is your opinion? Is the code bellow really a vanilla webcomponent? I see extending from HTMLElement (vanilla) but importing lit-html.

import { html, render } from 'lit-html';
export default class ExampleVanilla extends HTMLElement {...

Thread Thread
bennypowers profile image
Benny Powers ðŸ‡ŪðŸ‡ąðŸ‡ĻðŸ‡Ķ Author

Puts on open-wc hat ðŸŽĐ

At the moment open-wc certainly recommends lit-element. I don't think that's likely to change in the future, but if course it may. Surely, for now we think it's the best way to go.

However, while we think you'll have the best DX and performance with lit-element, our tools support any web components - after all web components are about interoperability.

Takes off open-wc hat ðŸĶē

I totally relate to your concerns about framework lock. Don't worry though: custom elements and the DOM provide strong encapsulation for your components' APIs. At work, were slowly migrating our front end away from angularjs. It's painful mostly because of the lack of DOM support. We're making it work though, with things like ng-custom-element to enable us to pass objects from the angular scope to the DOM.

Inside components we've already migrated to lit-element, however, it's a different story. We are free to (and do) bring in components from other libraries as needed.

An approach I take is to select by id in my shadow CSS rather than tag name, in case we decide to swap out a component's implementation later on.

- my-button {
+ #submit-button {

Is it the best idea to load multiple web component libraries on the front end? Probably not - we don't need the extra bundle bloat, it's nice to align around one interface on a team. We need to move fast though. If the datepicker we need is implemented with polymer instead of lit-element, that's fine and dandy. We'll bring in the polymer version today, and if we find the time later, maybe will implement it ourselves with lit, or swap it for a newer 3rd party version. And WRT bundle size, we could fit 10 lit-elements in the bundle space of one angularjs, so we're already winning.

So I don't think if lit-element as a "framework" per-se. They only non-standards APIs it gives are templating, converting attributes, and observed properties, and all if those are provided as mixins to HTMLElement. If we work the whole thing with some shiny new API like the very-cool hybrids or haunted, it wouldn't be so easy to replace a component's base class (or more broadly, helper library). If we opted for something magical and bespoke like svelte, all the moreso.

Putting it another way, if you're starting from scratch, much of the knowledge you'll gain learning lit-element will apply to other web component libraries and frameworks, where the reverse might not be true.

tl;dr: lit-element stays "closer to the metal" while patching some features missing from the browser and smoothing over some rough APIs that are there. That's the level of abstraction that my team has found most helpful.

Thread Thread
jimisdrpc profile image
jimisdrpc

Hello Benny. Have you tried code a WebComponent splitted in two files (javascript and html)? If so, how do you uit test it? Maybe you could try give your opinion how to fix github.com/open-wc/open-wc/issues/730 or stackoverflow.com/questions/575024.... The problem is I didn't find some way to unit test when I have a Vanilla WebComponent separated in two files, html and javascript. In github.com/jimisdrpc/skyscanner-op... you find a webcomponent composed by two files: src/skyscanner-flight-search/skyscanner-flight-search.html and src/skyscanner-flight-search/skyscanner-flight-search.js. If you try my unit test test/skyscanner-flight-search.test.js you will see that window.customElements.whenDefined('skyscanner-flight-search') will never be resolved. Anyl trick how uniit test a Vanilla WebComponent splitted int two files will be appreciatted.

Thread Thread
bennypowers profile image
Benny Powers ðŸ‡ŪðŸ‡ąðŸ‡ĻðŸ‡Ķ Author

Hello,

I didn't look too deeply into your code, it looks like you've solved most of the problems already. It did seem like you were having a bit of trouble with one of your unit tests, though.

Try this:

describe('skyscanner flight search', function() {
  it('show div', async function() {
    const el = await fixture(html`<skyscanner-flight-search></skyscanner-flight-search>`);
    console.debug('before promise');
    await window.customElements.whenDefined('skyscanner-flight-search')
    console.debug('after promise');
    expect(el).to.exist;
  });
});
Thread Thread
jimisdrpc profile image
jimisdrpc

Thanks for you promptly answer. Well, you removed the most relevant part of my test: el.shadowRoot.querySelector('#firstdiv2');. Basically, I want to check if there is a div with id firstdiv2 and it must fail since the correct id is firstdiv. Your suggestion will pass but, as fafr as I ccan see, you are just checking if the fixture works; you aren't checking anything from the webcomponent html.

Thread Thread
jimisdrpc profile image
jimisdrpc

Based on your suggestion, I found a solution:

import { html, fixture, expect } from '@open-wc/testing';

import '../src/skyscanner-flight-search/skyscanner-flight-search.js';

describe('skyscanner flight search', () => {
it('show div', async() => {
const el = await fixture(html
<skyscanner-flight-search></skyscanner-flight-search>
);
await window.customElements.whenDefined('skyscanner-flight-search')
expect(el.shadowRoot.querySelector('#firstdiv')).to.exist;

});

it('show input for session key', async() => {
    const el = await fixture(html `
  <skyscanner-flight-search></skyscanner-flight-search>
`);
    await window.customElements.whenDefined('skyscanner-flight-search')
    expect(el.shadowRoot.querySelector('#inputSessionKey')).to.exist;
});

});

Thread Thread
jimisdrpc profile image
jimisdrpc

What is your opinion about how I am testing? IN few words, it is based on Karma + Mocha and depending on fixture approaches.

Thread Thread
bennypowers profile image
Benny Powers ðŸ‡ŪðŸ‡ąðŸ‡ĻðŸ‡Ķ Author

if your goal is to test that the shadow root renders the way you expect, I suggest using the open-wc package semantic-dom-diff, which is built-in to open-wc's testing setup, like so:

import { expect, fixture } from `@open-wc/testing`;

describe('skyscanner flight search', function() {
  it('should render the correct Shadow DOM', async function() {
    const el = await fixture(`<skyscanner-flight-search></skyscanner-flight-search>`);
    await window.customElements.whenDefined('skyscanner-flight-search')
    expect(el).shadowDom.to.equal(`
      <!-- this dom string will be semantically compared to the real dom -->
      <!-- comments will be stripped out -->
      <!-- and you'll get a helpful diff as otuput if you use open-wc's testing setup -->
      <div id="firstDiv"></div>
      <input id="inputSessionKey"/>
    `);
  });
});