10 KiB
		
	
	
	
	
	
			
		
		
	
	Style guides and linting
See the relevant style guides for our guidelines and for information on linting:
JavaScript
We defer to Airbnb on most style-related conventions and enforce them with eslint.
See our current .eslintrc for specific rules and patterns.
Common
ESlint
- 
Never disable eslint rules unless you have a good reason.
You may see a lot of legacy files with/* eslint-disable some-rule, some-other-rule */at the top, but legacy files are a special case. Any time you develop a new feature or refactor an existing one, you should abide by the eslint rules. - 
Never Ever EVER disable eslint globally for a file
 
  // bad
  /* eslint-disable */
  // better
  /* eslint-disable some-rule, some-other-rule */
  // best
  // nothing :)
- If you do need to disable a rule for a single violation, try to do it as locally as possible
 
  // bad
  /* eslint-disable no-new */
  import Foo from 'foo';
  new Foo();
  // better
  import Foo from 'foo';
  // eslint-disable-next-line no-new
  new Foo();
- 
There are few rules that we need to disable due to technical debt. Which are:
 - 
When they are needed always place ESlint directive comment blocks on the first line of a script, followed by any global declarations, then a blank newline prior to any imports or code.
 
  // bad
  /* global Foo */
  /* eslint-disable no-new */
  import Bar from './bar';
  // good
  /* eslint-disable no-new */
  /* global Foo */
  import Bar from './bar';
- 
Never disable the
no-undefrule. Declare globals with/* global Foo */instead. - 
When declaring multiple globals, always use one
/* global [name] */line per variable. 
  // bad
  /* globals Flash, Cookies, jQuery */
  // good
  /* global Flash */
  /* global Cookies */
  /* global jQuery */
- Use up to 3 parameters for a function or class. If you need more accept an Object instead.
 
  // bad
  fn(p1, p2, p3, p4) {}
  // good
  fn(options) {}
Modules, Imports, and Exports
- Use ES module syntax to import modules
 
  // bad
  require('foo');
  // good
  import Foo from 'foo';
  // bad
  module.exports = Foo;
  // good
  export default Foo;
- Relative paths: Unless you are writing a test, always reference other scripts using
relative paths instead of 
~ 
- 
In app/assets/javascripts:
// bad import Foo from '~/foo' // good import Foo from '../foo'; - 
In spec/javascripts:
// bad import Foo from '../../app/assets/javascripts/foo' // good import Foo from '~/foo'; 
- 
Avoid using IIFE. Although we have a lot of examples of files which wrap their contents in IIFEs (immediately-invoked function expressions), this is no longer necessary after the transition from Sprockets to webpack. Do not use them anymore and feel free to remove them when refactoring legacy code.
 - 
Avoid adding to the global namespace.
 
  // bad
  window.MyClass = class { /* ... */ };
  // good
  export default class MyClass { /* ... */ }
- Side effects are forbidden in any script which contains exports
 
  // bad
  export default class MyClass { /* ... */ }
  document.addEventListener("DOMContentLoaded", function(event) {
    new MyClass();
  }
Data Mutation and Pure functions
- Strive to write many small pure functions, and minimize where mutations occur.
 
  // bad
  const values = {foo: 1};
  function impureFunction(items) {
    const bar = 1;
    items.foo = items.a * bar + 2;
    return items.a;
  }
  const c = impureFunction(values);
  // good
  var values = {foo: 1};
  function pureFunction (foo) {
    var bar = 1;
    foo = foo * bar + 2;
    return foo;
  }
  var c = pureFunction(values.foo);
- 
Avoid constructors with side-effects
 - 
Prefer
.map,.reduceor.filterover.forEachA forEach will cause side effects, it will be mutating the array being iterated. Prefer using.map,.reduceor.filter 
  const users = [ { name: 'Foo' }, { name: 'Bar' } ];
  // bad
  users.forEach((user, index) => {
    user.id = index;
  });
  // good
  const usersWithId = users.map((user, index) => {
    return Object.assign({}, user, { id: index });
  });
Parse Strings into Numbers
parseInt()is preferable overNumber()or+
  // bad
  +'10' // 10
  // good
  Number('10') // 10
  // better
  parseInt('10', 10);
CSS classes used for JavaScript
- If the class is being used in Javascript it needs to be prepend with 
js- 
  // bad
  <button class="add-user">
    Add User
  </button>
  // good
  <button class="js-add-user">
    Add User
  </button>
Vue.js
Basic Rules
- The service has it's own file
 - The store has it's own file
 - Use a function in the bundle file to instantiate the Vue component:
 
  // bad
  class {
    init() {
      new Component({})
    }
  }
  // good
  document.addEventListener('DOMContentLoaded', () => new Vue({
    el: '#element',
    components: {
      componentName
    },
    render: createElement => createElement('component-name'),
  }));
- Don not use a singleton for the service or the store
 
  // bad
  class Store {
    constructor() {
      if (!this.prototype.singleton) {
        // do something
      }
    }
  }
  // good
  class Store {
    constructor() {
      // do something
    }
  }
Naming
- Extensions: Use 
.vueextension for Vue components. - Reference Naming: Use camelCase for their instances:
 
  // good
  import cardBoard from 'cardBoard'
  components: {
    cardBoard:
  };
- Props Naming: Avoid using DOM component prop names.
 - Props Naming: Use kebab-case instead of camelCase to provide props in templates.
 
  // bad
  <component class="btn">
  // good
  <component css-class="btn">
  // bad
  <component myProp="prop" />
  // good
  <component my-prop="prop" />
Alignment
- Follow these alignment styles for the template method:
 
  // bad
  <component v-if="bar"
      param="baz" />
  <button class="btn">Click me</button>
  // good
  <component
    v-if="bar"
    param="baz"
  />
  <button class="btn">
    Click me
  </button>
  // if props fit in one line then keep it on the same line
  <component bar="bar" />
Quotes
- Always use double quotes 
"inside templates and single quotes'for all other JS. 
  // bad
  template: `
    <button :class='style'>Button</button>
  `
  // good
  template: `
    <button :class="style">Button</button>
  `
Props
- Props should be declared as an object
 
  // bad
  props: ['foo']
  // good
  props: {
    foo: {
      type: String,
      required: false,
      default: 'bar'
    }
  }
- Required key should always be provided when declaring a prop
 
  // bad
  props: {
    foo: {
      type: String,
    }
  }
  // good
  props: {
    foo: {
      type: String,
      required: false,
      default: 'bar'
    }
  }
- Default key should always be provided if the prop is not required:
 
  // bad
  props: {
    foo: {
      type: String,
      required: false,
    }
  }
  // good
  props: {
    foo: {
      type: String,
      required: false,
      default: 'bar'
    }
  }
  // good
  props: {
    foo: {
      type: String,
      required: true
    }
  }
Data
datamethod should always be a function
  // bad
  data: {
    foo: 'foo'
  }
  // good
  data() {
    return {
      foo: 'foo'
    };
  }
Directives
- Shorthand 
@is preferable overv-on 
  // bad
  <component v-on:click="eventHandler"/>
  // good
  <component @click="eventHandler"/>
- Shorthand 
:is preferable overv-bind 
  // bad
  <component v-bind:class="btn"/>
  // good
  <component :class="btn"/>
Closing tags
- Prefer self closing component tags
 
  // bad
  <component></component>
  // good
  <component />
Ordering
- Order for a Vue Component:
 namepropsmixinsdirectivesdatacomponentscomputedPropsmethodsbeforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedactivateddeactivatedbeforeDestroydestroyed
Vue and Boostrap
- Tooltips: Do not rely on 
has-tooltipclass name for Vue components 
  // bad
  <span
    class="has-tooltip"
    title="Some tooltip text">
    Text
  </span>
  // good
  <span
    v-tooltip
    title="Some tooltip text">
    Text
  </span>
- 
Tooltips: When using a tooltip, include the tooltip directive,
./app/assets/javascripts/vue_shared/directives/tooltip.js - 
Don't change
data-original-title. 
  // bad
  <span data-original-title="tooltip text">Foo</span>
  // good
  <span title="tooltip text">Foo</span>
  $('span').tooltip('fixTitle');