Skip to content
This repository has been archived by the owner on Jul 7, 2022. It is now read-only.

Latest commit

 

History

History
195 lines (165 loc) · 4.12 KB

CONTRIBUTING.md

File metadata and controls

195 lines (165 loc) · 4.12 KB

Style Guide

This guide applies to client-rendered code in /app.

Eslint

Try to follow the Airbnb Javascript Guide.

The Airbnb eslint config has been enabled minus jsx-filename-extension and react/forbid-prop-types.

We also use Prettier.

Imports

Follow the pattern:

// absolute imports
import React from 'react'
import PropTypes from 'prop-types'

// then a newline
// then relative imports
import { MyComponent } from "../../MyComponent";

// then a newline
// then css modules
import styles from './MyStyles.scss'

Props

Stateless Functional Components

// destructure within function
// call the parameter props
const MyForm = props => {
  const { aProp, anotherProp } = props
  return (
    <div>
    </div>
  )
}

// include defaults for non-required
MyForm.defaultProps = {
  aProp: ''
}

// define a type for every prop
MyForm.propTypes = {
  anotherProp: PropTypes.number.isRequired,
  aProp: PropTypes.string
}

Classes that extend Component

export class MyClass extends Component {
  // use static proptypes
  static propTypes = {
    isLoading: PropTypes.bool,
    errors: PropTypes.arrayOf(string).isRequired
  };

  // and static defaultProps
  static defaultProps = {
    isLoading: false
  };
 
  // destructure inside render 
  render() {
    const { isLoading, errors } = this.props;

    return (
      <div>
      </div>
    )
  }

Pages and Content

Routes

Define the base page route in routes.js

<Switch>
  <Route path={`${rootPath}/myPage`} component={MyPage} />

Pages

Pages are classes, do sub-routing, redux, and pass props to content components.

Ensure you handle sub route default.

class MyPage extends Component {
  static propTypes = {
    propA: PropTypes.string.isRequired,
    propB: PropTypes.bool.isRequired
  }
  
  componentWillMount() {
    this.props.loadData()
  }
  
  render() {
    const { propA, propB } = this.props
    <Switch>
      <Route
        exact path={match.url}
        render={() =>
          <MyForm
            propA={propA}
            propB={propB}
          />}
      />
      <Route component={NotFound} /> 
    </Switch>
  }
}

const mapStateToProps = state => {
  return {
    propA: state.myValue
  }
}

const mapDispatchToProps = dispatch => {
  return {
    loadData: () => dispatch(myAction)
  }
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MyPage))

Content

Content components are stateless:

const MyForm = props => {
  const { propA, propB } = props
  return (
    <div>
    </div>
  )
}

MyForm.propTypes = {
  propA: PropTypes.string.isRequired,
  propB: PropTypes.bool.isRequired
}

Redux

Use a traditional redux structure.

Seperate files for actions, actions creators and reducers. Ensure the file names match.

Define Action Types in constants.js

export const ADD_TODO = 'ADD_TODO'

Use ES6 Action Creators

import { ADD_TODO } from '../../constants'

export const addTodo = text => ({
  type: ADD_TODO,
  text: 'hello world'
})

Use Spread Operator in Reducers

case ADD_TODO:
  return { 
    ...state,
    todos: [
      ...state.todos,
      {
        text: action.text,
        completed: false
      }
    ]
  })

CSS

The base styles come from uikit 2.

Modifications to base styles can be found in custom.css.

Use css modules for component-specific styles.

Ensure camelCase style names.

.loadingIndicator {
  position: relative;
  width: 60px; 
  height: 60px;
}

Tests

Where possible, provide at least a shallow snapshot test or render sanity check for a component.

Place tests alongside components. Ensure test file name matches component and ends with .test.js