Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(adr): add Developing and Publishing ClientSide Behaviours #250

Merged
@@ -1,4 +1,4 @@
# 1. Record architecture decisions
# 0246. Record architecture decisions
manuelpuyol marked this conversation as resolved.
Show resolved Hide resolved

Date: 2021-02-18

Expand Down
48 changes: 48 additions & 0 deletions adr/0250-developing-and-publishing-clientside-behaviours.md
@@ -0,0 +1,48 @@
# 0250. Developing and Publishing ClientSide Behaviours
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋🏻 one other thought here: have we considered if we might want to provide some sort of encapsulation of the Javascript we write for ViewComponents? I could see us coupling the two together in similar fashion to our explorations of CSS modules, generating a selector specifically for a component and using it in the JS.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our current architecture is leveraging Web Components, which are encapsulated by tag name. Attempting to define an already defined web component causes an exception on the front-end.

Web Components are encapsulated in that they are only activated for/on the pre-defined tags. In other words behaviour is not executed globally, only when an element enters the dom.

Not in the scope of this ADR but something I want to explore further is adding a suite of meta-tests which assert that the JS follows good conventions, such as:

  • The naming of JS files matches the naming of components.
  • The JS avoids global behaviours where applicable.
  • Any component with a JS file must have an integration test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attempting to define an already defined web component causes an exception on the front-end.

This is the aspect I'm curious about. Can we provide an architecture where this exception is raised at compile time or as part of a test suite?

(This isn't a blocker at all)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already implicitly part of our test suite as we include the compiled bundle in the system tests!


Date: 2021-02-19

## Status

Accepted

## Context

We want to ship client side behavioural elements with Primer View Components to increase the fidelity of our components.

Doing so requires us to align to a set of constraints for the Browser ecosystem, as well as the ecosystems that consumers of Primer View Components have:

- We need to produce a JavaScript based artefact, as this is the language browsers use for client side behaviours.
- The artefact should be easily consumable by existing tool chains of our consumers, which may or may not be the Rails Asset Pipeline.
- The setup process should be uncomplicated and familiar to consumers; by using existing Rails paradigms, or JavaScript ecosystem paradigms or tools such as npm.
- Client side behaviours should be well tested and offer the same level of quality the existing non-behavioural components do.

## Decision

### TypeScript

We will write behaviours in TypeScript, which is a compile-to-JavaScript language that offers stronger type guarantees leading to a higher quality level of code. TypeScript is already being used for [`primer/components`](https://github.com/primer/components) which is also a motivating factor.

Behaviours will live in TypeScript files in the `app/components` directory next to their respective `.rb` files. By convention files will be named with the same basename as the Ruby counterpart. An `app/components/primer.ts` file will also exist which must import all other `.ts` files, acting as an index file. A single `.js` file with no external dependencies, which will live in `app/assets/javascripts/primer.js`. This single file can then be loaded through the Rails Asset pipeline, allowing for consumers to call `javascript_include_tag("primer")`.
keithamus marked this conversation as resolved.
Show resolved Hide resolved

### Publishing to npm

Additionally, we will produce an [npm package](https://www.npmjs.com/) which contains all the compiled JavaScript both as individual files and as the single bundle in `app/assets/javascripts/primer.js`. This npm package will allow for consumption of the JS outside of using the traditional Rails Asset Pipeline. Developers will be able to call `import "@primer/view_components"` within their JS, which means their JS toolchain will be able to perform necessary transformations or optimisations. This will also allow for selective component imports, for example `import "@primer/view_components/time_ago_component"` could import just `TimeAgoComponent`.

Publishing a sidecar package like this moderately increases _complexity_ for this library, but drastically increases _flexibility_ and decreases the burden on the library to provide flexible paths for the variety of JavaScript compilers in use today. Good precedent exists for offering sidecar client side libraries, for example the [Turbo Framework offers a sidecar package](https://github.com/hotwired/turbo-rails#installation) if not using the built in Asset Pipeline.

### Testing

We will test our behaviours in the Browser environment (using Rails System tests), as this is where they will be consumed. This will require using Browser APIs to automate testing. We will use test automation libraries that automate browsers via the [Devtools Protocol](https://chromedevtools.github.io/devtools-protocol/) such as [Puppeteer](https://pptr.dev/), [Playwright](https://playwright.dev/) or [Ferrum](https://ferrum.rocks/). Using a Devtools Protocol based tool gives us the best level of flexibility in testing, while also being compatible with current browsers.

### Documentation

We will document which components are behavioural, so that consumers are aware they are required to load the client side bundle code for these components to work properly. We will offer a documentation page on how to use the `javascript_include_tag` helper to load the bundle using the Asset Pipeline, or otherwise import the npm package within their codebases.

## Consequences

This library will be able to provide behaviours for higher fidelity components, using consistent methods that lean to the strengths of the Rails & JS ecosystems.

Contributors of Primer View Components will need to have an understanding of TypeScript if they wish to author behavioural components.

There will be additional work for our consumers if they wish to use behavioural components. Consumers will need to use the `javascript_include_tag` helper, or otherwise import the npm bundle in order for these components to work in their projects.