diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2017-03-22 19:30:54 +0000 |
---|---|---|
committer | Alfredo Sumaran <alfredo@gitlab.com> | 2017-03-22 19:30:54 +0000 |
commit | 8c3bdc853a5237a3bef6e26fcf22132db7e8bd9c (patch) | |
tree | a862ed0847f6dc8d7ffe8dea7141714a51a8387d /doc/development/frontend.md | |
parent | 3574963bc01097da418b62ae6f87d02359f36f12 (diff) | |
download | gitlab-ce-8c3bdc853a5237a3bef6e26fcf22132db7e8bd9c.tar.gz |
Creates Frontend Style guide
Diffstat (limited to 'doc/development/frontend.md')
-rw-r--r-- | doc/development/frontend.md | 511 |
1 files changed, 2 insertions, 509 deletions
diff --git a/doc/development/frontend.md b/doc/development/frontend.md index 50105a486d0..f46cc377f95 100644 --- a/doc/development/frontend.md +++ b/doc/development/frontend.md @@ -1,511 +1,4 @@ -# Frontend Development Guidelines - -This document describes various guidelines to ensure consistency and quality -across GitLab's frontend team. - -## Overview - -GitLab is built on top of [Ruby on Rails][rails] using [Haml][haml] with -[Hamlit][hamlit]. Be wary of [the limitations that come with using -Hamlit][hamlit-limits]. We also use [SCSS][scss] and plain JavaScript with -[ES6 by way of Babel][es6]. - -The asset pipeline is [Sprockets][sprockets], which handles the concatenation, -minification, and compression of our assets. - -[jQuery][jquery] is used throughout the application's JavaScript, with -[Vue.js][vue] for particularly advanced, dynamic elements. - -### Architecture - -The Frontend Architect is an expert who makes high-level frontend design choices -and decides on technical standards, including coding standards, and frameworks. - -When you are assigned a new feature that requires architectural design, -make sure it is discussed with one of the Frontend Architecture Experts. - -This rule also applies if you plan to change the architecture of an existing feature. - -These decisions should be accessible to everyone, so please document it on the Merge Request. - -You can find the Frontend Architecture experts on the [team page][team-page]. - -You can find documentation about the desired architecture for a new feature built with Vue.js in [here][vue-section]. - -### Realtime - -When writing code for realtime features we have to keep a couple of things in mind: -1. Do not overload the server with requests. -1. It should feel realtime. - -Thus, we must strike a balance between sending requests and the feeling of realtime. -Use the following rules when creating realtime solutions. - -1. The server will tell you how much to poll by sending `Poll-Interval` in the header. -Use that as your polling interval. This way it is easy for system administrators to change the -polling rate. -A `Poll-Interval: -1` means you should disable polling, and this must be implemented. -1. A response with HTTP status `4XX` or `5XX` should disable polling as well. -1. Use a common library for polling. -1. Poll on active tabs only. Use a common library to find out which tab currently has eyes on it. -Please use [Focus](https://gitlab.com/andrewn/focus). Specifically [Eyeballs Detector](https://gitlab.com/andrewn/focus/blob/master/lib/eyeballs-detector.js). -1. Use regular polling intervals, do not use backoff polling, or jitter, as the interval will be -controlled by the server. -1. The backend code will most likely be using etags. You do not and should not check for status -`304 Not Modified`. The browser will transform it for you. - -### Vue - -For more complex frontend features, we recommend using Vue.js. It shares -some ideas with React.js as well as Angular. - -To get started with Vue, read through [their documentation][vue-docs]. - -#### How to build a new feature with Vue.js -**Components, Stores and Services** - -In some features implemented with Vue.js, like the [issue board][issue-boards] -or [environments table][environments-table] -you can find a clear separation of concerns: - -``` -new_feature -├── components -│ └── component.js.es6 -│ └── ... -├── store -│ └── new_feature_store.js.es6 -├── service -│ └── new_feature_service.js.es6 -├── new_feature_bundle.js.es6 -``` -_For consistency purposes, we recommend you to follow the same structure._ - -Let's look into each of them: - -**A `*_bundle.js` file** - -This is the index file of your new feature. This is where the root Vue instance -of the new feature should be. - -The Store and the Service should be imported and initialized in this file and provided as a prop to the main component. - -Don't forget to follow [these steps.][page_specific_javascript] - -**A folder for Components** - -This folder holds all components that are specific of this new feature. -If you need to use or create a component that will probably be used somewhere -else, please refer to `vue_shared/components`. - -A good thumb rule to know when you should create a component is to think if -it will be reusable elsewhere. - -For example, tables are used in a quite amount of places across GitLab, a table -would be a good fit for a component. -On the other hand, a table cell used only in on table, would not be a good use -of this pattern. - -You can read more about components in Vue.js site, [Component System][component-system] - -**A folder for the Store** - -The Store is a class that allows us to manage the state in a single -source of truth. - -The concept we are trying to follow is better explained by Vue documentation -itself, please read this guide: [State Management][state-management] - -**A folder for the Service** - -The Service is used only to communicate with the server. -It does not store or manipulate any data. -We use [vue-resource][vue-resource-repo] to -communicate with the server. - -The [issue boards service][issue-boards-service] -is a good example of this pattern. - -## Performance - -### Resources - -- [WebPage Test][web-page-test] for testing site loading time and size. -- [Google PageSpeed Insights][pagespeed-insights] grades web pages and provides feedback to improve the page. -- [Profiling with Chrome DevTools][google-devtools-profiling] -- [Browser Diet][browser-diet] is a community-built guide that catalogues practical tips for improving web page performance. - -### Page-specific JavaScript - -Certain pages may require the use of a third party library, such as [d3][d3] for -the User Activity Calendar and [Chart.js][chartjs] for the Graphs pages. These -libraries increase the page size significantly, and impact load times due to -bandwidth bottlenecks and the browser needing to parse more JavaScript. - -In cases where libraries are only used on a few specific pages, we use -"page-specific JavaScript" to prevent the main `application.js` file from -becoming unnecessarily large. - -Steps to split page-specific JavaScript from the main `application.js`: - -1. Create a directory for the specific page(s), e.g. `graphs/`. -1. In that directory, create a `namespace_bundle.js` file, e.g. `graphs_bundle.js`. -1. In `graphs_bundle.js` add the line `//= require_tree .`, this adds all other files in the directory to the bundle. -1. Add any necessary libraries to `app/assets/javascripts/lib/`, all files directly descendant from this directory will be precompiled as separate assets, in this case `chart.js` would be added. -1. Add the new "bundle" file to the list of precompiled assets in -`config/application.rb`. - - For example: `config.assets.precompile << "graphs/graphs_bundle.js"`. -1. Move code reliant on these libraries into the `graphs` directory. -1. In the relevant views, add the scripts to the page with the following: - -```haml -- content_for :page_specific_javascripts do - = page_specific_javascript_tag('lib/chart.js') - = page_specific_javascript_tag('graphs/graphs_bundle.js') -``` - -The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js` -is separated from the bundle file so it can be cached separately from the bundle -and reused for other pages that also rely on the library. For an example, see -[this Haml file][page-specific-js-example]. - -### Minimizing page size - -A smaller page size means the page loads faster (especially important on mobile -and poor connections), the page is parsed more quickly by the browser, and less -data is used for users with capped data plans. - -General tips: - -- Don't add new fonts. -- Prefer font formats with better compression, e.g. WOFF2 is better than WOFF, which is better than TTF. -- Compress and minify assets wherever possible (For CSS/JS, Sprockets does this for us). -- If some functionality can reasonably be achieved without adding extra libraries, avoid them. -- Use page-specific JavaScript as described above to dynamically load libraries that are only needed on certain pages. - -## Accessibility - -### Resources - -[Chrome Accessibility Developer Tools][chrome-accessibility-developer-tools] -are useful for testing for potential accessibility problems in GitLab. - -Accessibility best-practices and more in-depth information is available on -[the Audit Rules page][audit-rules] for the Chrome Accessibility Developer Tools. - -## Security - -### Resources - -[Mozilla’s HTTP Observatory CLI][observatory-cli] and the -[Qualys SSL Labs Server Test][qualys-ssl] are good resources for finding -potential problems and ensuring compliance with security best practices. - -<!-- Uncomment these sections when CSP/SRI are implemented. -### Content Security Policy (CSP) - -Content Security Policy is a web standard that intends to mitigate certain -forms of Cross-Site Scripting (XSS) as well as data injection. - -Content Security Policy rules should be taken into consideration when -implementing new features, especially those that may rely on connection with -external services. - -GitLab's CSP is used for the following: - -- Blocking plugins like Flash and Silverlight from running at all on our pages. -- Blocking the use of scripts and stylesheets downloaded from external sources. -- Upgrading `http` requests to `https` when possible. -- Preventing `iframe` elements from loading in most contexts. - -Some exceptions include: - -- Scripts from Google Analytics and Piwik if either is enabled. -- Connecting with GitHub, Bitbucket, GitLab.com, etc. to allow project importing. -- Connecting with Google, Twitter, GitHub, etc. to allow OAuth authentication. - -We use [the Secure Headers gem][secure_headers] to enable Content -Security Policy headers in the GitLab Rails app. - -Some resources on implementing Content Security Policy: - -- [MDN Article on CSP][mdn-csp] -- [GitHub’s CSP Journey on the GitHub Engineering Blog][github-eng-csp] -- The Dropbox Engineering Blog's series on CSP: [1][dropbox-csp-1], [2][dropbox-csp-2], [3][dropbox-csp-3], [4][dropbox-csp-4] - -### Subresource Integrity (SRI) - -Subresource Integrity prevents malicious assets from being provided by a CDN by -guaranteeing that the asset downloaded is identical to the asset the server -is expecting. - -The Rails app generates a unique hash of the asset, which is used as the -asset's `integrity` attribute. The browser generates the hash of the asset -on-load and will reject the asset if the hashes do not match. - -All CSS and JavaScript assets should use Subresource Integrity. For implementation details, -see the documentation for [the Sprockets implementation of SRI][sprockets-sri]. - -Some resources on implementing Subresource Integrity: - -- [MDN Article on SRI][mdn-sri] -- [Subresource Integrity on the GitHub Engineering Blog][github-eng-sri] - ---> - -### Including external resources - -External fonts, CSS, and JavaScript should never be used with the exception of -Google Analytics and Piwik - and only when the instance has enabled it. Assets -should always be hosted and served locally from the GitLab instance. Embedded -resources via `iframes` should never be used except in certain circumstances -such as with ReCaptcha, which cannot be used without an `iframe`. - -### Avoiding inline scripts and styles - -In order to protect users from [XSS vulnerabilities][xss], we will disable inline scripts in the future using Content Security Policy. - -While inline scripts can be useful, they're also a security concern. If -user-supplied content is unintentionally left un-sanitized, malicious users can -inject scripts into the web app. - -Inline styles should be avoided in almost all cases, they should only be used -when no alternatives can be found. This allows reusability of styles as well as -readability. - -## Style guides and linting - -See the relevant style guides for our guidelines and for information on linting: - -- [SCSS][scss-style-guide] -- JavaScript - We defer to [AirBnb][airbnb-js-style-guide] on most style-related -conventions and enforce them with eslint. See [our current .eslintrc][eslintrc] -for specific rules and patterns. - -## Testing - -Feature tests need to be written for all new features. Regression tests -also need to be written for all bug fixes to prevent them from occurring -again in the future. - -See [the Testing Standards and Style Guidelines](testing.md) for more -information. - -### Running frontend tests - -`rake karma` runs the frontend-only (JavaScript) tests. -It consists of two subtasks: - -- `rake karma:fixtures` (re-)generates fixtures -- `rake karma:tests` actually executes the tests - -As long as the fixtures don't change, `rake karma:tests` is sufficient -(and saves you some time). - -Please note: Not all of the frontend fixtures are generated. Some are still static -files. These will not be touched by `rake karma:fixtures`. - -## Design Patterns - -### Singletons - -When exactly one object is needed for a given task, prefer to define it as a -`class` rather than as an object literal. Prefer also to explicitly restrict -instantiation, unless flexibility is important (e.g. for testing). - -```javascript -// bad - -gl.MyThing = { - prop1: 'hello', - method1: () => {} -}; - -// good - -class MyThing { - constructor() { - this.prop1 = 'hello'; - } - method1() {} -} - -gl.MyThing = new MyThing(); - -// best - -let singleton; - -class MyThing { - constructor() { - if (!singleton) { - singleton = this; - singleton.init(); - } - return singleton; - } - - init() { - this.prop1 = 'hello'; - } - - method1() {} -} - -gl.MyThing = MyThing; - -``` - -### Manipulating the DOM in a JS Class - -When writing a class that needs to manipulate the DOM guarantee a container option is provided. -This is useful when we need that class to be instantiated more than once in the same page. - -Bad: -```javascript -class Foo { - constructor() { - document.querySelector('.bar'); - } -} -new Foo(); -``` - -Good: -```javascript -class Foo { - constructor(opts) { - document.querySelector(`${opts.container} .bar`); - } -} - -new Foo({ container: '.my-element' }); -``` -You can find an example of the above in this [class][container-class-example]; - -## Supported browsers - -For our currently-supported browsers, see our [requirements][requirements]. - - -## Gotchas - -### Spec errors due to use of ES6 features in `.js` files - -If you see very generic JavaScript errors (e.g. `jQuery is undefined`) being -thrown in Karma, Spinach, or Rspec tests but can't reproduce them manually, -you may have included `ES6`-style JavaScript in files that don't have the -`.js.es6` file extension. Either use ES5-friendly JavaScript or rename the file -you're working in (`git mv <file.js> <file.js.es6>`). - -### Spec errors due to use of unsupported JavaScript - -Similar errors will be thrown if you're using JavaScript features not yet -supported by our test runner's version of webkit, whether or not you've updated -the file extension. Examples of unsupported JavaScript features are: - -- Array.from -- Array.find -- Array.first -- Object.assign -- Async functions -- Generators -- Array destructuring -- For Of -- Symbol/Symbol.iterator -- Spread - -Until these are polyfilled or transpiled appropriately, they should not be used. -Please update this list with additional unsupported features or when any of -these are made usable. - -### Spec errors due to JavaScript not enabled - -If, as a result of a change you've made, a feature now depends on JavaScript to -run correctly, you need to make sure a JavaScript web driver is enabled when -specs are run. If you don't you'll see vague error messages from the spec -runner, and an explosion of vague console errors in the HTML snapshot. - -To enable a JavaScript driver in an `rspec` test, add `js: true` to the -individual spec or the context block containing multiple specs that need -JavaScript enabled: - -```ruby - -# For one spec -it 'presents information about abuse report', js: true do - # assertions... -end - -describe "Admin::AbuseReports", js: true do - it 'presents information about abuse report' do - # assertions... - end - it 'shows buttons for adding to abuse report' do - # assertions... - end -end -``` - -In Spinach, the JavaScript driver is enabled differently. In the `*.feature` -file for the failing spec, add the `@javascript` flag above the Scenario: - -``` -@javascript -Scenario: Developer can approve merge request - Given I am a "Shop" developer - And I visit project "Shop" merge requests page - And merge request 'Bug NS-04' must be approved - And I click link "Bug NS-04" - When I click link "Approve" - Then I should see approved merge request "Bug NS-04" - -``` +# Frontend Development Guidelines -[rails]: http://rubyonrails.org/ -[haml]: http://haml.info/ -[hamlit]: https://github.com/k0kubun/hamlit -[hamlit-limits]: https://github.com/k0kubun/hamlit/blob/master/REFERENCE.md#limitations -[scss]: http://sass-lang.com/ -[es6]: https://babeljs.io/ -[sprockets]: https://github.com/rails/sprockets -[jquery]: https://jquery.com/ -[vue]: http://vuejs.org/ -[vue-docs]: http://vuejs.org/guide/index.html -[web-page-test]: http://www.webpagetest.org/ -[pagespeed-insights]: https://developers.google.com/speed/pagespeed/insights/ -[google-devtools-profiling]: https://developers.google.com/web/tools/chrome-devtools/profile/?hl=en -[browser-diet]: https://browserdiet.com/ -[d3]: https://d3js.org/ -[chartjs]: http://www.chartjs.org/ -[page-specific-js-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/13bb9ed77f405c5f6ee4fdbc964ecf635c9a223f/app/views/projects/graphs/_head.html.haml#L6-8 -[chrome-accessibility-developer-tools]: https://github.com/GoogleChrome/accessibility-developer-tools -[audit-rules]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules -[observatory-cli]: https://github.com/mozilla/http-observatory-cli -[qualys-ssl]: https://www.ssllabs.com/ssltest/analyze.html -[secure_headers]: https://github.com/twitter/secureheaders -[mdn-csp]: https://developer.mozilla.org/en-US/docs/Web/Security/CSP -[github-eng-csp]: http://githubengineering.com/githubs-csp-journey/ -[dropbox-csp-1]: https://blogs.dropbox.com/tech/2015/09/on-csp-reporting-and-filtering/ -[dropbox-csp-2]: https://blogs.dropbox.com/tech/2015/09/unsafe-inline-and-nonce-deployment/ -[dropbox-csp-3]: https://blogs.dropbox.com/tech/2015/09/csp-the-unexpected-eval/ -[dropbox-csp-4]: https://blogs.dropbox.com/tech/2015/09/csp-third-party-integrations-and-privilege-separation/ -[mdn-sri]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity -[github-eng-sri]: http://githubengineering.com/subresource-integrity/ -[sprockets-sri]: https://github.com/rails/sprockets-rails#sri-support -[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting -[scss-style-guide]: scss_styleguide.md -[requirements]: ../install/requirements.md#supported-web-browsers -[issue-boards]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/boards -[environments-table]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/environments -[page_specific_javascript]: https://docs.gitlab.com/ce/development/frontend.html#page-specific-javascript -[component-system]: https://vuejs.org/v2/guide/#Composing-with-Components -[state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch -[vue-resource-repo]: https://github.com/pagekit/vue-resource -[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6 -[airbnb-js-style-guide]: https://github.com/airbnb/javascript -[eslintrc]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.eslintrc -[team-page]: https://about.gitlab.com/team -[vue-section]: https://docs.gitlab.com/ce/development/frontend.html#how-to-build-a-new-feature-with-vue-js -[container-class-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/mini_pipeline_graph_dropdown.js +This page has moved [here](fe_guide/index.md). |