Writing Components

Writing Components

This page explains how MarkBind components work, focused on implementation and testing.

MarkBind provides a number of components (e.g. expandable panels, tooltips) to dynamically express content. In order to serve content on the browser, MarkBind syntax is converted to valid HTML.

How are components in MarkBind syntax parsed and converted to HTML?




Implementing Components

There are multiple ways to implement MarkBind components.

Transforming the Node Directly

One way to implement a MarkBind component is to transform the node itself. This is a more low-level implementation that can be useful when a node only needs to be modified slightly.

When a node is processed, MarkBind syntax is converted to HTML, and any remaining attributes will also be converted to HTML attributes. This can be useful if you just need to add a HTML attribute to the node, or modify the value of an existing attribute.

These transformations may take place at various stages of node processing: before (preProcessNode), during (processNode), or after (postProcessNode).

Examples

  • Adding a class to a node (setting line numbers for code blocks)
  • Modifying attributes and adding a directive to a node (former implementation of popovers)

Vue Components

Many MarkBind components are implemented as Vue components, either by creating a component in the vue-components package, or by importing a component from an external library. This can be useful when a more complicated set of features is needed, where a Vue component can provide an interface for us to manage these functionalities.

Vue components are registered in vue-components/src/index.js, which allows them to be used in the template section of any Vue instance without needing to be imported first.

How do MarkBind attributes/slots get passed to the Vue component?



Examples

As a Plugin

MarkBind components can be implemented as a plugin as well. This is suitable for more lightweight components where the implementation is largely in processing the node, making it fitting to use MarkBind plugins' processNode or postRender interfaces. These interfaces provide additional entry points for modifying the page generated, and do not replace MarkBind's usual node processing.

The Writing Plugins page is a good guide to get started on plugins.

Examples

Testing Components

Automated tests that are relevant to the components include:

The API for Snapshot tests can be found at Vue Test Utils.

Additionally, it's a good idea to check the deployed PR preview in addition to serving the app locally, to ensure that there are no differences.

Additional Considerations

Some things you may need to consider when implementing a MarkBind component:

Reactivity

Reactivity refers to the ability of a web framework to update your view whenever the application state has changed. It is important to consider reactivity when implementing a component that may have dynamic contents that readers can interact with (e.g. opening a panel, triggering a tooltip to show).

SSR

Components should be compatible with SSR (Server-Side Rendering). Minimally, there should be no SSR issues (viewable from the browser console), though a lack of warnings does not mean that there are no SSR problems. A guide on SSR for MarkBind can be found here.

Vue-specific tips for resolving SSR issues:

  • The mount and beforeMount lifecycle hooks will only be executed on the client, not the server
  • When using v-if, ensure that it will evaluate to the same value on both the client and server
  • Take note of how the Vue component will be compiled, ensuring that the HTML is correct and aligns on both client- and server- side
  • Conditionally render data when it has been fully loaded

Bundle size

When creating a new component, you may need to import a package or library to support some functionality. Ideally, this should not increase MarkBind's bundle size too much. Bundlephobia may be useful to quickly look up the size of a package!

Dependencies

When choosing to use a third-party library or package, it should ideally be well-maintained and not have too many dependencies. While dependencies may be inevitable, a package with dependencies on large libraries may lag behind the most recent releases of these libraries, which may become a blocker for MarkBind to migrate to these recent releases as well.

For instance, if bootstrap-vue depends on Bootstrap and Vue, we will need to wait for bootstrap-vue to migrate to the newest versions of both Bootstrap and Vue before MarkBind can migrate to these versions of Bootstrap and Vue as well.

Feel free to raise any concerns during the initial discussion phase for other devs to weigh in on the tradeoffs!

Attributes and Slots

MarkBind components may support , or both. Some components allow users to supply the same content as either a slot or an attribute. If an author provides the same content as both a slot and an attribute, in most cases, the slot should override the attribute.

MarkBind should also log a warning to inform the author of this conflict!