top of page

Tutorials and Code Examples

Using Stencil with Velo

Fri Apr 08 2022

2.gif
Blank_Canvas_on_Transparent_Background.p

Share

  • Twitter
  • Black LinkedIn Icon
  • Facebook

How we used Stencil and Velo to create a custom Web Component for an upcoming project.

At Wix we try to use our own products, and Velo is no exception. This effort is so critical to Wix that it even got it’s own name - Wix on Wix. For example, we try to build different screens using Velo instead of using React and our own Wix Style React library. This is the story of one such effort.


We are working on a project called ‘Site Branches’. Since the project is not released to the general public at the time of this writing, we won’t share too many details about what it does, and only mention that we tried building the Branches Table component using Velo.


Here is what the Branches Table should look like:

Trying to build the component using Velo and the drag and drop editor failed.


As you can see in the screenshot above, the menu that expands when you click the ellipse is cut off because the repeater item (the light blue band) has a CSS overflow property of hidden.


Before declaring this effort a complete failure, we decided to investigate another option - building the table as a web component using Stencil.


Stencil


Stencil is a React-inspired web component library. It uses the same JSX as React and some of the same concepts (Render function, props, state). However, Stencil differs as it compiles to an optimal bundle, creating a Virtual DOM that is consolidated directly into the DOM itself (no VDOM to VDOM comparison). The result is standard web components with optimal performance.


On top, Stencil provides a good development experience through their template applications for building a component, a website / app or a PWA. Stencil also supports server-side rendering and hydrations for websites that need to be optimized for indexing.


Setting up Stencil for Velo


Here is the setup we used for the Stencil project:

To generate a bundle for Velo, we needed to modify the stencil.config.ts file to include inlineDynamicImports: true and externalRuntime: false for the dist-custom-elements-bundle target to create a single file bundle.


We also removed the dist target.


The stencil-config.ts file looked like this:

Then we started building our component using regular stencil commands:

At this point, we started developing our component as a standard Stencil component.


Coding the branches-table element


Coding the component was done using Stencil’s out of the box tools. The complete component source is on github.


We created a component as two Stencil components, one for the table and one for the actions button (with the actions popup) - nothing special here.


Let's do a walkthrough of the component code -


First we declared the branches-table component:

The above tells Stencil that:

  • The web component’s name (tag name) is branches-table

  • The stylesheet file to include is branches-table.css

  • It uses shadow dome

  • It has property and attribute branches which are of type string (web component attributes are always strings - while stencil allows more freedom, Velo restricts us to using string only)

  • It gets a reference to the actual DOM element as the el property (more on that later).


The render function is:

As can be seen above, we have a div for the header line, a table for the actual table itself and another div for the footer line.


The header table uses css display: flex; justify-content: space-between; to spread the two child divs to the sides, while the footer uses display: flex; justify-content: center; to center the Create New Branch action.


The actual table rows are rendered using the renderTableContent method below:

Here we can see simple rendering of values, with some formatting utilities.


The interesting part of this function is the usage of the action-button web component. We pass the actual actions into the action-button as divs, and the action-button renders those divs into a slot in the popup menu. More about that below.


Another important point is the use of the copyIcon, renameIcon, clockIcon and deleteIcon functions, which render SVG icons. The source of those functions look like the following:

export const renameIcon = () => <svg viewBox="0 0 18 18" fill="currentColor" width="18" height="18"><path d="M5,8 L5,6 C5,5.44771525 5.44771525,5 6,5 L13,5 C13.5522847,5 14,5.44771525 14,6 L14,8 L13,8 L13,6 L10,6 L10,13 L11.997142,13 L11.997142,14 L7,14 L7,13 L9,13 L9,6 L6,6 L6,8 L5,8 Z"></path></svg>

Finally, we bind events to functions of the element using the arrow function notation.


Coding the action-button element


The action button element displays its children in a popup menu that opens above the button. Its render function is quite simple:

The popup is implemented as two divs that are rendered only if the popup is open. The two divs are positioned and styled using CSS to create the popup experience.


First, the click-trap div is styled to appear over the whole screen using a fixed position. Any click on it closes the popup:

Second, the menu itself is displayed using an absolute position relative to the action-button-root element. The arrow is designed using CSS and is generated using the great css arrow please service. The resulting CSS is:

Importing the Component to Velo


To use the component in Velo, we need to take care of three tasks:

  1. Handle input attributes

  2. Post events back to Velo

  3. Import the component to Velo and write the code to interact with it.


Handle input attributes: This first one is simple - by using the @Prop() annotation on the branches member in branches-table we define an attribute that Velo can use.


Post events back to Velo: To post events back to Velo when a user clicks on any of the actions, like the Help link, or the Create New Branch link, we use dispatchEvent on the custom element dom element instance.


We get the instance of the dom element of the custom element using the @Element annotation:

To post an event to Velo we then use:

Import the component to Velo and write the code to interact with it: To import the component to Velo, we run build and then generate the script:

Then, we copy the generated code from the dist/custom-elements/index.js file to the Velo file: public/custom-elements/branches-table.js. The generated file from Stencil requires two additional things for it to be used in Velo - babel polyfill and registration of the custom elements.


We get the polyfill from the @babel/polyfill NPM package - which we then need to install on Velo. Then we create a file to import the polyfill and register the custom element - we call this file public/custom-elements/register-branches-table.js

To finish importing the component to Velo, we add a Custom Element in the Editor from Add > Embed > Custom Element. In the Element Settings, we select Velo file as the way we’ll add our script file, the register-branches-table.js file as the script file in Velo, and give our custom element the branches-tableTag Name.



Finally, we need to write code that interacts with our custom element. We do that on the page where we placed our custom element, in the $w.onReady function for its page code (which is called when the Velo page model is ready). We write the following code to set attributes or listen to events.

Summary


The actual Velo site with the branches table can be seen here. The Stencil project is on github here.


With this example we demonstrate how we can extend Velo by creating Web Components that are embedded in a Velo site as Custom Elements. In this case we used Stencil as a library for coding the Web Component, but any other library for a Web Component would work as well.


Will we be using the branches-table as part of the Wix on Wix screen for the branches project? At this point we can say that we have managed to create the experience we looked for and are still working on building the branches project.


Blank_Canvas_on_Transparent_Background.p

0

get certified.png

Related Posts

View All

1.png

CORVID LIBARY

Unified Database Management

Unified Database Management

Jun 18, 2018   8min

1.png

CORVID LIBARY

Unified Database Management

Unified Database Management

Jun 18, 2018   8min

1.png

CORVID LIBARY

Unified Database Management

Unified Database Management

Jun 18, 2018   8min

bottom of page