Development workflow

Setup

Set up your Chi development environment with Docker. First make sure you have Docker installed, then run the command:

$ scripts/docker.sh alias
$ source ~/.bash_profile

Now your environment is set up. To start development run:

$ chi start

Once the container has been bootstrapped and the Chi project has started, connect to http://localhost:8000/ in your browser to load Chi. While running, any changes to the Chi source will be automatically reloaded in your browser.

Testing

End-to-end tests

Cypress is used for end-to-end testing. Tests are located in the cypress/integration folder. To make them run:

$ chi test-e2e

You can run end-to-end tests interactively by launching the start command and opening a local version of the Cypress application. Then select the ux-chi root folder as the project folder.

Visual regression tests

Chi has visual regression tests build for each component that are implemented in BackstopJS. Each Custom Element component MUST have a Custom Element version for the same test files that are implemented in the normal version of Chi. These tests are configured in the backstop-non-responsive-ce.json file.

E.g. test/chi/custom-elements/icons.pug will be compared to test/chi/components/icons.pug to ensure both components are rendered in the same way.

This tests are run by the common test command

$ chi test

Reports are available in the reports/html_report/non_responsive_ce/ folder.

Style guide

This style guide is based on the one used by Ionic team.

File structure

  • One component per file.
  • One component per directory. Though it may make sense to group similar components into the same directory, it's easier to document components when each one has its own directory.
  • Implementation (.tsx) and styles of a component should live in the same directory.
├── icon
│   ├── icon.scss
│   ├── icon.tsx
├── card
│   ├── card.scss
│   ├── card.tsx
│   └── test (to be defined)
├── card-content
│   ├── card-content.scss
│   └── card-content.tsx
├── card-title
│   ├── card-title.scss
│   ├── card-title.tsx

Naming

HTML tag

PREFIX

The prefix has a major role when you are creating a collection of components intended to be used across different projects. Web Components are not scoped because they are globally declared within the webpage, which means a "unique" prefix is needed to prevent collisions and can help to quickly identify the collection. Additionally, web components are required to contain a "-" dash within the tag name, so using the first section to namespace your components is a natural fit.

Tag prefix must be chi:

<chi-button>
<chi-header>

NAME

Components are not actions, they are conceptually "things". It is better to use nouns instead of verbs, such as "animation" instead of "animating". Here are some good examples of component names: "input", "tab", "nav", and "menu".

MODIFIERS

When several components are related and/or coupled, it is a good practice to use common naming with different modifiers, for example:

<chi-card>
<chi-card-header>
<chi-card-content>

Component (TS class)

The name of the ES6 class of the component SHOULD NOT have a prefix since classes are scoped. There is no risk of collision.

@Component({
  tag: 'chi-button'
})
export class Button { ... }

@Component({
  tag: 'chi-icon'
})
export class Icon { ... }

TypeScript

Follow tslint-ionic-rules. This file is located in /src/custom-elements/ and has to be configured in your editor.

Variable decorators should be inlined.

@Prop() name: string;
@Element() el: HTMLElement;

Method decorator should be multi-line

@Listen('click')
onClick() {
  ...
}

Use private variables and methods as much as possible; they are useful to detect dead code and enforce encapsulation. Note that this is a feature which TypeScript provides to help hardening your code, but using private, public or protected does not make a difference in the actual JavaScript output.

Code with Method/Prop/Event/Component decorators should have jsdocs; this allows documentation generation and for a better user experience in an editor that has TypeScript intellisense. Stencil auto-generated documentation will be included in the Chi documentation pages.

Properties

It is considered a good practice and SHOULD be configured to Reflect public properties to attributes.

@Prop({ reflect: true }) color: string;

Properties SHOULDN'T be configured as mutable and, once a value is set by a user, the component should not update it internally. Use it only if strictly necessary.

@Prop({ mutable: true}) color: string;

Properties SHOULD have a default value and MUST be validated.

Sometimes it is enough validation the use of a definition type:

@Prop() bordered: boolean = false;

Other times a function with a @Watch decorator is necessary:

@Watch('name')
  validateName(newValue: string, oldValue: string) {
    const isBlank = typeof newValue == null;
    const has2chars = typeof newValue === 'string' && newValue.length >= 2;
    if (isBlank) { throw new Error('name: required') };
    if (!has2chars ) { throw new Error('name: has2chars') };
  }

Styling

Chi components will have scoped CSS, to prevent collisions when using in non-chi compatible environments.

@Component({
  tag: 'chi-icon',
  styleUrl: 'icon.scss',
  scoped: true
})

As of now, there is no support for per-version scoped CSS. This means that rules from two different versions of chi components will collide, so it should be avoided.

Rules

  • Components MIGHT have a *.scss file that will import the necessary files from the project
  • All rules but font-face ones MUST be wrapped inside a :host rule to override a possible Chi default stylesheet
  • The default display for Custom Elements is inline so the developer MUST take care of this.
@import 'components/icons/webfont-font-face';
:host(chi-icon) {
@import '../../global/styles/common';
@extend %root-typography;
@import 'components/icons/icons';
@import 'components/icons/webfont-icons';

display: inline-block;
}