Commit 2a03d152 authored by Amy Qualls's avatar Amy Qualls

Merge branch 'lkerr-master-patch-32593' into 'master'

FE development guideline improvements based on Vale warnings

See merge request gitlab-org/gitlab!52394
parents f8d50e8f 2f0a4224
...@@ -9,11 +9,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -9,11 +9,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Resources ## Resources
[Chrome Accessibility Developer Tools](https://github.com/GoogleChrome/accessibility-developer-tools) [Chrome Accessibility Developer Tools](https://github.com/GoogleChrome/accessibility-developer-tools)
are useful for testing for potential accessibility problems in GitLab. assist with testing for potential accessibility problems in GitLab.
The [axe](https://www.deque.com/axe/) browser extension (available for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/) and [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd)) is The [axe](https://www.deque.com/axe/) browser extension (available for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/) and [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd)) provides running audits and feedback on markup, CSS, and even potentially problematic color usages.
also a handy tool for running audits and getting feedback on markup, CSS and even potentially problematic color usages.
Accessibility best-practices and more in-depth information are available on Accessibility best-practices and more in-depth information are available on
[the Audit Rules page](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules) for the Chrome Accessibility Developer Tools. The [Awesome Accessibility](https://github.com/brunopulis/awesome-a11y) list is also a [the Audit Rules page](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules) for the Chrome Accessibility Developer Tools. The [Awesome Accessibility](https://github.com/brunopulis/awesome-a11y) list is a compilation of accessibility-related material.
useful compilation of accessibility-related material.
...@@ -6,9 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -6,9 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Architecture # Architecture
When you are developing a new feature that requires architectural design, or if When developing a feature that requires architectural design, or changing the fundamental design of an existing feature, discuss it with a Frontend Architecture Expert.
you are changing the fundamental design of an existing feature, make sure it is
discussed with one of the Frontend Architecture Experts.
A Frontend Architect is an expert who makes high-level Frontend design decisions A Frontend Architect is an expert who makes high-level Frontend design decisions
and decides on technical standards, including coding standards and frameworks. and decides on technical standards, including coding standards and frameworks.
......
...@@ -44,7 +44,7 @@ Advantages over [`spyOn()`](https://jasmine.github.io/api/edge/global.html#spyOn ...@@ -44,7 +44,7 @@ Advantages over [`spyOn()`](https://jasmine.github.io/api/edge/global.html#spyOn
- no need to create response objects - no need to create response objects
- does not allow call through (which we want to avoid) - does not allow call through (which we want to avoid)
- simple API to test error cases - clear API to test error cases
- provides `replyOnce()` to allow for different responses - provides `replyOnce()` to allow for different responses
We have also decided against using [Axios interceptors](https://github.com/axios/axios#interceptors) because they are not suitable for mocking. We have also decided against using [Axios interceptors](https://github.com/axios/axios#interceptors) because they are not suitable for mocking.
......
...@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
When exactly one object is needed for a given task, prefer to define it as a 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 `class` rather than as an object literal. Prefer also to explicitly restrict
instantiation, unless flexibility is important (e.g. for testing). instantiation, unless flexibility is important (such as for testing).
```javascript ```javascript
// bad // bad
...@@ -56,7 +56,7 @@ export default class MyThing { ...@@ -56,7 +56,7 @@ export default class MyThing {
## Manipulating the DOM in a JS Class ## Manipulating the DOM in a JS Class
When writing a class that needs to manipulate the DOM guarantee a container option is provided. 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. This can be used when we need that class to be instantiated more than once in the same page.
Bad: Bad:
......
...@@ -12,9 +12,9 @@ You can find more about the organization of the frontend team in the [handbook]( ...@@ -12,9 +12,9 @@ You can find more about the organization of the frontend team in the [handbook](
The idea is to remind us about specific topics during the time we build a new feature or start something. This is a common practice in other industries (like pilots) that also use standardized checklists to reduce problems early on. The idea is to remind us about specific topics during the time we build a new feature or start something. This is a common practice in other industries (like pilots) that also use standardized checklists to reduce problems early on.
Copy the content over to your issue or merge request and if something doesn't apply simply remove it from your current list. Copy the content over to your issue or merge request and if something doesn't apply, remove it from your current list.
This checklist is intended to help us during development of bigger features/refactorings, it's not a "use it always and every point always matches" list. This checklist is intended to help us during development of bigger features/refactorings. It is not a "use it always and every point always matches" list.
Please use your best judgment when to use it and please contribute new points through merge requests if something comes to your mind. Please use your best judgment when to use it and please contribute new points through merge requests if something comes to your mind.
...@@ -77,7 +77,7 @@ With the purpose of being [respectful of others' time](https://about.gitlab.com/ ...@@ -77,7 +77,7 @@ With the purpose of being [respectful of others' time](https://about.gitlab.com/
- includes tests - includes tests
- includes a changelog entry (when necessary) - includes a changelog entry (when necessary)
- Before assigning to a maintainer, assign to a reviewer. - Before assigning to a maintainer, assign to a reviewer.
- If you assigned a merge request or pinged someone directly, be patient because we work in different timezones and asynchronously. Unless the merge request is urgent (like fixing a broken master), please don't DM or reassign the merge request before waiting for a 24-hour window. - If you assigned a merge request or pinged someone directly, be patient because we work in different timezones and asynchronously. Unless the merge request is urgent (like fixing a broken default branch), please don't DM or reassign the merge request before waiting for a 24-hour window.
- If you have a question regarding your merge request/issue, make it on the merge request/issue. When we DM each other, we no longer have a SSOT and [no one else is able to contribute](https://about.gitlab.com/handbook/values/#public-by-default). - If you have a question regarding your merge request/issue, make it on the merge request/issue. When we DM each other, we no longer have a SSOT and [no one else is able to contribute](https://about.gitlab.com/handbook/values/#public-by-default).
- When you have a big **Draft** merge request with many changes, you're advised to get the review started before adding/removing significant code. Make sure it is assigned well before the release cut-off, as the reviewer(s)/maintainer(s) would always prioritize reviewing finished MRs before the **Draft** ones. - When you have a big **Draft** merge request with many changes, you're advised to get the review started before adding/removing significant code. Make sure it is assigned well before the release cut-off, as the reviewer(s)/maintainer(s) would always prioritize reviewing finished MRs before the **Draft** ones.
- Make sure to remove the `Draft:` title before the last round of review. - Make sure to remove the `Draft:` title before the last round of review.
......
...@@ -65,7 +65,7 @@ The editor follows the same public API as [provided by Monaco editor](https://mi ...@@ -65,7 +65,7 @@ The editor follows the same public API as [provided by Monaco editor](https://mi
1. Editor's loading state. 1. Editor's loading state.
Editor Lite comes with the loading state built-in, making spinners and loaders rarely needed in HTML. To benefit the built-in loading state, set the `data-editor-loading` property on the HTML element that is supposed to contain the editor. Editor Lite will show the loader automatically while it's bootstrapping. Editor Lite comes with the loading state built-in, making spinners and loaders rarely needed in HTML. To benefit the built-in loading state, set the `data-editor-loading` property on the HTML element that is supposed to contain the editor. Editor Lite shows the loader automatically while it's bootstrapping.
![Editor Lite: loading state](img/editor_lite_loading.png) ![Editor Lite: loading state](img/editor_lite_loading.png)
1. Update syntax highlighting if the filename changes. 1. Update syntax highlighting if the filename changes.
...@@ -89,7 +89,7 @@ form.addEventListener('submit', () => { ...@@ -89,7 +89,7 @@ form.addEventListener('submit', () => {
1. Performance 1. Performance
Even though Editor Lite itself is extremely slim, it still depends on Monaco editor. Monaco is not an easily tree-shakeable module. Hence, every time you add Editor Lite to a view, the JavaScript bundle's size significantly increases, affecting your view's loading performance. To avoid that, it is recommended to import the editor on demand on those views where it is not 100% certain that the editor will be used. Or if the editor is a secondary element of the view. Loading Editor Lite on demand is no different from loading any other module: Even though Editor Lite itself is extremely slim, it still depends on Monaco editor. Monaco is not an easily tree-shakeable module. Hence, every time you add Editor Lite to a view, the JavaScript bundle's size significantly increases, affecting your view's loading performance. It is recommended to import the editor on demand on those views where it is not 100% certain that the editor is needed. Or if the editor is a secondary element of the view. Loading Editor Lite on demand is no different from loading any other module:
```javascript ```javascript
someActionFunction() { someActionFunction() {
...@@ -109,8 +109,8 @@ which would not depend on any particular group. Even though the Editor Lite's co ...@@ -109,8 +109,8 @@ which would not depend on any particular group. Even though the Editor Lite's co
[Create::Editor FE Team](https://about.gitlab.com/handbook/engineering/development/dev/create-editor/), [Create::Editor FE Team](https://about.gitlab.com/handbook/engineering/development/dev/create-editor/),
the main functional elements — extensions — can be owned by any group. Editor Lite extensions' main idea the main functional elements — extensions — can be owned by any group. Editor Lite extensions' main idea
is that the core of the editor remains very slim and stable. At the same time, whatever new functionality is that the core of the editor remains very slim and stable. At the same time, whatever new functionality
is needed can be added as an extension to this core, without touching the core itself. It allows any group is needed can be added as an extension to this core, without touching the core itself. Any group is allowed
to build and own any new editing functionality without being afraid of it being broken or overridden with to build and own new editing functionality without being afraid of it being broken or overridden with
the Editor Lite changes. the Editor Lite changes.
Structurally, the complete implementation of Editor Lite could be presented as the following diagram: Structurally, the complete implementation of Editor Lite could be presented as the following diagram:
...@@ -145,7 +145,7 @@ Important things to note here: ...@@ -145,7 +145,7 @@ Important things to note here:
### Using an existing extension ### Using an existing extension
Adding an extension to Editor Lite's instance is simple: Adding an extension to Editor Lite's instance requires the following steps:
```javascript ```javascript
import EditorLite from '~/editor/editor_lite'; import EditorLite from '~/editor/editor_lite';
...@@ -159,7 +159,7 @@ editor.use(MyExtension); ...@@ -159,7 +159,7 @@ editor.use(MyExtension);
### Creating an extension ### Creating an extension
Let's create our first Editor Lite extension. As aforementioned, extensions are ES6 modules exporting the simple `Object` that is used to extend Editor Lite's functionality. As the most straightforward test, let's create an extension that extends Editor Lite with a new function that, when called, will output editor's content in `alert`. Let's create our first Editor Lite extension. Extensions are ES6 modules exporting a basic `Object` that is used to extend Editor Lite's functionality. As a test, let's create an extension that extends Editor Lite with a new function that, when called, outputs editor's content in `alert`.
`~/my_folder/my_fancy_extension.js:` `~/my_folder/my_fancy_extension.js:`
......
...@@ -25,7 +25,7 @@ when your platform does not support it. ...@@ -25,7 +25,7 @@ when your platform does not support it.
- `app/assets/images/emoji.png` - `app/assets/images/emoji.png`
- `app/assets/images/emoji@2x.png` - `app/assets/images/emoji@2x.png`
1. Ensure you see new individual images copied into `app/assets/images/emoji/` 1. Ensure you see new individual images copied into `app/assets/images/emoji/`
1. Ensure you can see the new emojis and their aliases in the GFM Autocomplete 1. Ensure you can see the new emojis and their aliases in the GitLab Flavored Markdown (GFM) Autocomplete
1. Ensure you can see the new emojis and their aliases in the award emoji menu 1. Ensure you can see the new emojis and their aliases in the award emoji menu
1. You might need to add new emoji Unicode support checks and rules for platforms 1. You might need to add new emoji Unicode support checks and rules for platforms
that do not support a certain emoji and we need to fallback to an image. that do not support a certain emoji and we need to fallback to an image.
......
...@@ -21,7 +21,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -21,7 +21,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## FAQ ## FAQ
### 1. How do I find the Rails route for a page? ### 1. How does one find the Rails route for a page?
#### Check the 'page' data attribute #### Check the 'page' data attribute
...@@ -36,7 +36,7 @@ Find here the [source code setting the attribute](https://gitlab.com/gitlab-org/ ...@@ -36,7 +36,7 @@ Find here the [source code setting the attribute](https://gitlab.com/gitlab-org/
#### Rails routes #### Rails routes
The `rake routes` command can be used to list all the routes available in the application, piping the output into `grep`, we can perform a search through the list of available routes. The `rake routes` command can be used to list all the routes available in the application. Piping the output into `grep`, we can perform a search through the list of available routes.
The output includes the request types available, route parameters and the relevant controller. The output includes the request types available, route parameters and the relevant controller.
```shell ```shell
...@@ -46,13 +46,13 @@ bundle exec rake routes | grep "issues" ...@@ -46,13 +46,13 @@ bundle exec rake routes | grep "issues"
### 2. `modal_copy_button` vs `clipboard_button` ### 2. `modal_copy_button` vs `clipboard_button`
The `clipboard_button` uses the `copy_to_clipboard.js` behavior, which is The `clipboard_button` uses the `copy_to_clipboard.js` behavior, which is
initialized on page load, so if there are vue-based clipboard buttons that initialized on page load. Vue clipboard buttons that
don't exist at page load (such as ones in a `GlModal`), they do not have the don't exist at page load (such as ones in a `GlModal`) do not have
click handlers associated with the clipboard package. click handlers associated with the clipboard package.
`modal_copy_button` was added that manages an instance of the `modal_copy_button` manages an instance of the
[`clipboard` plugin](https://www.npmjs.com/package/clipboard) specific to [`clipboard` plugin](https://www.npmjs.com/package/clipboard) specific to
the instance of that component, which means that clipboard events are the instance of that component. This means that clipboard events are
bound on mounting and destroyed when the button is, mitigating the above bound on mounting and destroyed when the button is, mitigating the above
issue. It also has bindings to a particular container or modal ID issue. It also has bindings to a particular container or modal ID
available, to work with the focus trap created by our GlModal. available, to work with the focus trap created by our GlModal.
...@@ -60,7 +60,7 @@ available, to work with the focus trap created by our GlModal. ...@@ -60,7 +60,7 @@ available, to work with the focus trap created by our GlModal.
### 3. A `gitlab-ui` component not conforming to [Pajamas Design System](https://design.gitlab.com/) ### 3. A `gitlab-ui` component not conforming to [Pajamas Design System](https://design.gitlab.com/)
Some [Pajamas Design System](https://design.gitlab.com/) components implemented in Some [Pajamas Design System](https://design.gitlab.com/) components implemented in
`gitlab-ui` do not conform with the design system specs because they lack some `gitlab-ui` do not conform with the design system specs. This is because they lack some
planned features or are not correctly styled yet. In the Pajamas website, a planned features or are not correctly styled yet. In the Pajamas website, a
banner on top of the component examples indicates that: banner on top of the component examples indicates that:
...@@ -77,18 +77,17 @@ It makes codebase unified and more comfortable to maintain/refactor in the futur ...@@ -77,18 +77,17 @@ It makes codebase unified and more comfortable to maintain/refactor in the futur
Ensure a [Product Designer](https://about.gitlab.com/company/team/?department=ux-department) Ensure a [Product Designer](https://about.gitlab.com/company/team/?department=ux-department)
reviews the use of the non-conforming component as part of the MR review. Make a reviews the use of the non-conforming component as part of the MR review. Make a
follow up issue and attach it to the component implementation epic found within the follow up issue and attach it to the component implementation epic found in the
[Components of Pajamas Design System epic](https://gitlab.com/groups/gitlab-org/-/epics/973). [Components of Pajamas Design System epic](https://gitlab.com/groups/gitlab-org/-/epics/973).
### 4. My submit form button becomes disabled after submitting ### 4. My submit form button becomes disabled after submitting
If you are using a submit button inside a form and you attach an `onSubmit` event listener on the form element, [this piece of code](https://gitlab.com/gitlab-org/gitlab/blob/794c247a910e2759ce9b401356432a38a4535d49/app/assets/javascripts/main.js#L225) adds a `disabled` class selector to the submit button when the form is submitted. A Submit button inside of a form attaches an `onSubmit` event listener on the form element. [This code](https://gitlab.com/gitlab-org/gitlab/blob/794c247a910e2759ce9b401356432a38a4535d49/app/assets/javascripts/main.js#L225) adds a `disabled` class selector to the submit button when the form is submitted. To avoid this behavior, add the class `js-no-auto-disable` to the button.
To avoid this behavior, add the class `js-no-auto-disable` to the button.
### 5. Should I use a full URL (i.e. `gon.gitlab_url`) or a full path (i.e. `gon.relative_url_root`) when referencing backend endpoints? ### 5. Should one use a full URL (for example `gon.gitlab_url`) or a full path (for example `gon.relative_url_root`) when referencing backend endpoints?
It's preferred to use a **full path** over a **full URL** because the URL uses the hostname configured with It's preferred to use a **full path** over a **full URL**. This is because the URL uses the hostname configured with
GitLab which may not match the request. This causes [CORS issues like this Web IDE one](https://gitlab.com/gitlab-org/gitlab/-/issues/36810). GitLab which may not match the request. This causes [cross-origin resource sharing issues like this Web IDE example](https://gitlab.com/gitlab-org/gitlab/-/issues/36810).
Example: Example:
...@@ -117,7 +116,7 @@ Example: ...@@ -117,7 +116,7 @@ Example:
### 6. How should the Frontend reference Backend paths? ### 6. How should the Frontend reference Backend paths?
We prefer not to add extra coupling by hardcoding paths. If possible, We prefer not to add extra coupling by hard-coding paths. If possible,
add these paths as data attributes to the DOM element being referenced in the JavaScript. add these paths as data attributes to the DOM element being referenced in the JavaScript.
Example: Example:
...@@ -153,7 +152,7 @@ export const fetchFoos = ({ state }) => { ...@@ -153,7 +152,7 @@ export const fetchFoos = ({ state }) => {
}; };
``` ```
### 7. How can I test the production build locally? ### 7. How can one test the production build locally?
Sometimes it's necessary to test locally what the frontend production build would produce, to do so the steps are: Sometimes it's necessary to test locally what the frontend production build would produce, to do so the steps are:
...@@ -161,7 +160,7 @@ Sometimes it's necessary to test locally what the frontend production build woul ...@@ -161,7 +160,7 @@ Sometimes it's necessary to test locally what the frontend production build woul
1. Open `gitlab.yaml` located in your `gitlab` installation folder, scroll down to the `webpack` section and change `dev_server` to `enabled: false`. 1. Open `gitlab.yaml` located in your `gitlab` installation folder, scroll down to the `webpack` section and change `dev_server` to `enabled: false`.
1. Run `yarn webpack-prod && gdk restart rails-web`. 1. Run `yarn webpack-prod && gdk restart rails-web`.
The production build takes a few minutes to be completed; any code changes at this point are The production build takes a few minutes to be completed. Any code changes at this point are
displayed only after executing the item 3 above again. displayed only after executing the item 3 above again.
To return to the normal development mode: To return to the normal development mode:
...@@ -176,8 +175,8 @@ To return to the normal development mode: ...@@ -176,8 +175,8 @@ To return to the normal development mode:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/28837) in GitLab 12.8. > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/28837) in GitLab 12.8.
GitLab has enabled the Babel `preset-env` option GitLab has enabled the Babel `preset-env` option
[`useBuiltIns: 'usage'`](https://babeljs.io/docs/en/babel-preset-env#usebuiltins-usage), [`useBuiltIns: 'usage'`](https://babeljs.io/docs/en/babel-preset-env#usebuiltins-usage).
which adds the appropriate `core-js` polyfills once for each JavaScript feature This adds the appropriate `core-js` polyfills once for each JavaScript feature
we're using that our target browsers don't support. You don't need to add `core-js` we're using that our target browsers don't support. You don't need to add `core-js`
polyfills manually. polyfills manually.
......
This diff is collapsed.
...@@ -100,7 +100,7 @@ by the examples that follow: ...@@ -100,7 +100,7 @@ by the examples that follow:
- `container` (optional): wraps the loading icon in a container, which centers the loading icon using the `text-center` CSS property. - `container` (optional): wraps the loading icon in a container, which centers the loading icon using the `text-center` CSS property.
- `color` (optional): either `orange` (default), `light`, or `dark`. - `color` (optional): either `orange` (default), `light`, or `dark`.
- `size` (optional): either `sm` (default), `md`, `lg`, or `xl`. - `size` (optional): either `sm` (default), `md`, `lg`, or `xl`.
- `css_class` (optional): defaults to an empty string, but can be useful for utility classes to fine-tune alignment or spacing. - `css_class` (optional): defaults to an empty string, but can be used for utility classes to fine-tune alignment or spacing.
**Example 1:** **Example 1:**
...@@ -164,8 +164,8 @@ export default { ...@@ -164,8 +164,8 @@ export default {
## SVG Illustrations ## SVG Illustrations
Please use from now on for any SVG based illustrations simple `img` tags to show an illustration by simply using either `image_tag` or `image_path` helpers. From now on, use `img` tags to display any SVG based illustrations with either `image_tag` or `image_path` helpers.
Please use the class `svg-content` around it to ensure nice rendering. Using the class `svg-content` around it ensures nice rendering.
### Usage in HAML/Rails ### Usage in HAML/Rails
......
...@@ -11,7 +11,7 @@ across the GitLab frontend team. ...@@ -11,7 +11,7 @@ across the GitLab frontend team.
## Overview ## Overview
GitLab is built on top of [Ruby on Rails](https://rubyonrails.org) using [Haml](https://haml.info/) and also a JavaScript based Frontend with [Vue.js](https://vuejs.org). GitLab is built on top of [Ruby on Rails](https://rubyonrails.org). It uses [Haml](https://haml.info/) and a JavaScript0based frontend with [Vue.js](https://vuejs.org).
Be wary of [the limitations that come with using Hamlit](https://github.com/k0kubun/hamlit/blob/master/REFERENCE.md#limitations). We also use [SCSS](https://sass-lang.com) and plain JavaScript with Be wary of [the limitations that come with using Hamlit](https://github.com/k0kubun/hamlit/blob/master/REFERENCE.md#limitations). We also use [SCSS](https://sass-lang.com) and plain JavaScript with
modern ECMAScript standards supported through [Babel](https://babeljs.io/) and ES module support through [webpack](https://webpack.js.org/). modern ECMAScript standards supported through [Babel](https://babeljs.io/) and ES module support through [webpack](https://webpack.js.org/).
...@@ -21,7 +21,7 @@ Working with our frontend assets requires Node (v10.13.0 or greater) and Yarn ...@@ -21,7 +21,7 @@ Working with our frontend assets requires Node (v10.13.0 or greater) and Yarn
### Browser Support ### Browser Support
For our currently-supported browsers, see our [requirements](../../install/requirements.md#supported-web-browsers). For supported browsers, see our [requirements](../../install/requirements.md#supported-web-browsers).
Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers. Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers.
Sign in to BrowserStack with the credentials saved in the **Engineering** vault of the GitLab Sign in to BrowserStack with the credentials saved in the **Engineering** vault of the GitLab
......
...@@ -230,12 +230,12 @@ To improve the time to first render we are using lazy loading for images. This w ...@@ -230,12 +230,12 @@ To improve the time to first render we are using lazy loading for images. This w
the actual image source on the `data-src` attribute. After the HTML is rendered and JavaScript is loaded, the actual image source on the `data-src` attribute. After the HTML is rendered and JavaScript is loaded,
the value of `data-src` is moved to `src` automatically if the image is in the current viewport. the value of `data-src` is moved to `src` automatically if the image is in the current viewport.
- Prepare images in HTML for lazy loading by renaming the `src` attribute to `data-src` AND adding the class `lazy`. - Prepare images in HTML for lazy loading by renaming the `src` attribute to `data-src` and adding the class `lazy`.
- If you are using the Rails `image_tag` helper, all images are lazy-loaded by default unless `lazy: false` is provided. - If you are using the Rails `image_tag` helper, all images are lazy-loaded by default unless `lazy: false` is provided.
If you are asynchronously adding content which contains lazy images then you need to call the function When asynchronously adding content which contains lazy images, call the function
`gl.lazyLoader.searchLazyImages()` which searches for lazy images and loads them if needed. `gl.lazyLoader.searchLazyImages()` which searches for lazy images and loads them if needed.
But in general it should be handled automatically through a `MutationObserver` in the lazy loading function. In general, it should be handled automatically through a `MutationObserver` in the lazy loading function.
### Animations ### Animations
...@@ -243,7 +243,7 @@ Only animate `opacity` & `transform` properties. Other properties (such as `top` ...@@ -243,7 +243,7 @@ Only animate `opacity` & `transform` properties. Other properties (such as `top`
Layout to be recalculated, which is much more expensive. For details on this, see "Styles that Affect Layout" in Layout to be recalculated, which is much more expensive. For details on this, see "Styles that Affect Layout" in
[High Performance Animations](https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/). [High Performance Animations](https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/).
If you _do_ need to change layout (for example, a sidebar that pushes main content over), prefer [FLIP](https://aerotwist.com/blog/flip-your-animations/) to change expensive If you _do_ need to change layout (for example, a sidebar that pushes main content over), prefer [FLIP](https://aerotwist.com/blog/flip-your-animations/). FLIP allows you to change expensive
properties once, and handle the actual animation with transforms. properties once, and handle the actual animation with transforms.
## Reducing Asset Footprint ## Reducing Asset Footprint
...@@ -251,7 +251,7 @@ properties once, and handle the actual animation with transforms. ...@@ -251,7 +251,7 @@ properties once, and handle the actual animation with transforms.
### Universal code ### Universal code
Code that is contained in `main.js` and `commons/index.js` is loaded and Code that is contained in `main.js` and `commons/index.js` is loaded and
run on _all_ pages. **DO NOT ADD** anything to these files unless it is truly run on _all_ pages. **Do not add** anything to these files unless it is truly
needed _everywhere_. These bundles include ubiquitous libraries like `vue`, needed _everywhere_. These bundles include ubiquitous libraries like `vue`,
`axios`, and `jQuery`, as well as code for the main navigation and sidebar. `axios`, and `jQuery`, as well as code for the main navigation and sidebar.
Where possible we should aim to remove modules from these bundles to reduce our Where possible we should aim to remove modules from these bundles to reduce our
...@@ -277,9 +277,9 @@ manually generated webpack bundles. However under this new system you should ...@@ -277,9 +277,9 @@ manually generated webpack bundles. However under this new system you should
not ever need to manually add an entry point to the `webpack.config.js` file. not ever need to manually add an entry point to the `webpack.config.js` file.
NOTE: NOTE:
If you are unsure what controller and action corresponds to a given page, you When unsure what controller and action corresponds to a page,
can find this out by inspecting `document.body.dataset.page` in your inspect `document.body.dataset.page` in your
browser's developer console while on any page in GitLab. browser's developer console from any page in GitLab.
#### Important Considerations #### Important Considerations
...@@ -294,7 +294,7 @@ browser's developer console while on any page in GitLab. ...@@ -294,7 +294,7 @@ browser's developer console while on any page in GitLab.
All GitLab JavaScript files are added with the `defer` attribute. All GitLab JavaScript files are added with the `defer` attribute.
According to the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer), According to the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer),
this implies that "the script is meant to be executed after the document has this implies that "the script is meant to be executed after the document has
been parsed, but before firing `DOMContentLoaded`". Since the document is already been parsed, but before firing `DOMContentLoaded`". Because the document is already
parsed, `DOMContentLoaded` is not needed to bootstrap applications because all parsed, `DOMContentLoaded` is not needed to bootstrap applications because all
the DOM nodes are already at our disposal. the DOM nodes are already at our disposal.
...@@ -366,9 +366,9 @@ browser's developer console while on any page in GitLab. ...@@ -366,9 +366,9 @@ browser's developer console while on any page in GitLab.
### Code Splitting ### Code Splitting
For any code that does not need to be run immediately upon page load, (for example, Code that does not need to be run immediately upon page load (for example,
modals, dropdowns, and other behaviors that can be lazy-loaded), you can split modals, dropdowns, and other behaviors that can be lazy-loaded) should be split
your module into asynchronous chunks with dynamic import statements. These into asynchronous chunks with dynamic import statements. These
imports return a Promise which is resolved after the script has loaded: imports return a Promise which is resolved after the script has loaded:
```javascript ```javascript
...@@ -377,16 +377,16 @@ import(/* webpackChunkName: 'emoji' */ '~/emoji') ...@@ -377,16 +377,16 @@ import(/* webpackChunkName: 'emoji' */ '~/emoji')
.catch(/* report error */) .catch(/* report error */)
``` ```
Please try to use `webpackChunkName` when generating these dynamic imports as Use `webpackChunkName` when generating dynamic imports as
it provides a deterministic filename for the chunk which can then be cached it provides a deterministic filename for the chunk which can then be cached
the browser across GitLab versions. in the browser across GitLab versions.
More information is available in [webpack's code splitting documentation](https://webpack.js.org/guides/code-splitting/#dynamic-imports). More information is available in [webpack's code splitting documentation](https://webpack.js.org/guides/code-splitting/#dynamic-imports).
### Minimizing page size ### Minimizing page size
A smaller page size means the page loads faster (especially important on mobile A smaller page size means the page loads faster, especially on mobile
and poor connections), the page is parsed more quickly by the browser, and less and poor connections. The page is parsed more quickly by the browser, and less
data is used for users with capped data plans. data is used for users with capped data plans.
General tips: General tips:
......
...@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Resources ## Resources
[Mozilla’s HTTP Observatory CLI](https://github.com/mozilla/http-observatory-cli) and the [Mozilla’s HTTP Observatory CLI](https://github.com/mozilla/http-observatory-cli) and
[Qualys SSL Labs Server Test](https://www.ssllabs.com/ssltest/analyze.html) are good resources for finding [Qualys SSL Labs Server Test](https://www.ssllabs.com/ssltest/analyze.html) are good resources for finding
potential problems and ensuring compliance with security best practices. potential problems and ensuring compliance with security best practices.
...@@ -76,7 +76,7 @@ such as with reCAPTCHA, which cannot be used without an `iframe`. ...@@ -76,7 +76,7 @@ such as with reCAPTCHA, which cannot be used without an `iframe`.
In order to protect users from [XSS vulnerabilities](https://en.wikipedia.org/wiki/Cross-site_scripting), we intend to disable In order to protect users from [XSS vulnerabilities](https://en.wikipedia.org/wiki/Cross-site_scripting), we intend to disable
inline scripts in the future using Content Security Policy. inline scripts in the future using Content Security Policy.
While inline scripts can be useful, they're also a security concern. If While inline scripts can make something easier, they're also a security concern. If
user-supplied content is unintentionally left un-sanitized, malicious users can user-supplied content is unintentionally left un-sanitized, malicious users can
inject scripts into the web app. inject scripts into the web app.
......
...@@ -111,7 +111,7 @@ preferred editor (all major editors are supported) accordingly. We suggest ...@@ -111,7 +111,7 @@ preferred editor (all major editors are supported) accordingly. We suggest
setting up Prettier to run when each file is saved. For instructions about using setting up Prettier to run when each file is saved. For instructions about using
Prettier in your preferred editor, see the [Prettier documentation](https://prettier.io/docs/en/editors.html). Prettier in your preferred editor, see the [Prettier documentation](https://prettier.io/docs/en/editors.html).
Please take care that you only let Prettier format the same file types as the global Yarn script does (`.js`, `.vue`, `.graphql`, and `.scss`). In VSCode by example you can easily exclude file formats in your settings file: Please take care that you only let Prettier format the same file types as the global Yarn script does (`.js`, `.vue`, `.graphql`, and `.scss`). For example, you can exclude file formats in your Visual Studio Code settings file:
```json ```json
"prettier.disableLanguages": [ "prettier.disableLanguages": [
...@@ -128,13 +128,13 @@ The following yarn scripts are available to do global formatting: ...@@ -128,13 +128,13 @@ The following yarn scripts are available to do global formatting:
yarn prettier-staged-save yarn prettier-staged-save
``` ```
Updates all currently staged files (based on `git diff`) with Prettier and saves the needed changes. Updates all staged files (based on `git diff`) with Prettier and saves the needed changes.
```shell ```shell
yarn prettier-staged yarn prettier-staged
``` ```
Checks all currently staged files (based on `git diff`) with Prettier and log which files would need manual updating to the console. Checks all staged files (based on `git diff`) with Prettier and log which files would need manual updating to the console.
```shell ```shell
yarn prettier-all yarn prettier-all
......
...@@ -22,7 +22,7 @@ All new features built with Vue.js must follow a [Flux architecture](https://fac ...@@ -22,7 +22,7 @@ All new features built with Vue.js must follow a [Flux architecture](https://fac
The main goal we are trying to achieve is to have only one data flow and only one data entry. The main goal we are trying to achieve is to have only one data flow and only one data entry.
In order to achieve this goal we use [vuex](#vuex). In order to achieve this goal we use [vuex](#vuex).
You can also read about this architecture in Vue docs about You can also read about this architecture in Vue documentation about
[state management](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch) [state management](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch)
and about [one way data flow](https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow). and about [one way data flow](https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow).
...@@ -70,7 +70,7 @@ The advantage of providing data from the DOM to the Vue instance through `props` ...@@ -70,7 +70,7 @@ The advantage of providing data from the DOM to the Vue instance through `props`
function instead of querying the DOM inside the main Vue component is avoiding the need to create a function instead of querying the DOM inside the main Vue component is avoiding the need to create a
fixture or an HTML element in the unit test, which makes the tests easier. fixture or an HTML element in the unit test, which makes the tests easier.
See the following example, also, please refer to our [Vue style guide](style/vue.md#basic-rules) for See the following example. Also, please refer to our [Vue style guide](style/vue.md#basic-rules) for
additional information on why we explicitly declare the data being passed into the Vue app; additional information on why we explicitly declare the data being passed into the Vue app;
```javascript ```javascript
...@@ -101,8 +101,8 @@ across the codebase. ...@@ -101,8 +101,8 @@ across the codebase.
#### Accessing the `gl` object #### Accessing the `gl` object
When we need to query the `gl` object for data that doesn't change during the application's life We query the `gl` object for data that doesn't change during the application's life
cycle, we should do it in the same place where we query the DOM. By following this practice, we can cycle in the same place we query the DOM. By following this practice, we can
avoid the need to mock the `gl` object, which makes tests easier. It should be done while avoid the need to mock the `gl` object, which makes tests easier. It should be done while
initializing our Vue instance, and the data should be provided as `props` to the main component: initializing our Vue instance, and the data should be provided as `props` to the main component:
...@@ -148,8 +148,8 @@ This approach has a few benefits: ...@@ -148,8 +148,8 @@ This approach has a few benefits:
- Arbitrarily deeply nested components can opt-in and access the flag without - Arbitrarily deeply nested components can opt-in and access the flag without
intermediate components being aware of it (c.f. passing the flag down via intermediate components being aware of it (c.f. passing the flag down via
props). props).
- Good testability, since the flag can be provided to `mount`/`shallowMount` - Good testability, because the flag can be provided to `mount`/`shallowMount`
from `vue-test-utils` simply as a prop. from `vue-test-utils` as a prop.
```javascript ```javascript
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
...@@ -207,20 +207,20 @@ Based on the Vue guidance: ...@@ -207,20 +207,20 @@ Based on the Vue guidance:
such as `user: new User()`. such as `user: new User()`.
- **Do not** add new JavaScript class implementations. - **Do not** add new JavaScript class implementations.
- **Do** use [GraphQL](../api_graphql_styleguide.md), [Vuex](vuex.md) or a set of components if - **Do** use [GraphQL](../api_graphql_styleguide.md), [Vuex](vuex.md) or a set of components if
cannot use simple primitives or objects. cannot use primitives or objects.
- **Do** maintain existing implementations using such approaches. - **Do** maintain existing implementations using such approaches.
- **Do** Migrate components to a pure object model when there are substantial changes to it. - **Do** Migrate components to a pure object model when there are substantial changes to it.
- **Do** add business logic to helpers or utils, so you can test them separately from your component. - **Do** add business logic to helpers or utilities, so you can test them separately from your component.
#### Why #### Why
There are additional reasons why having a JavaScript class presents maintainability issues on a huge codebase: There are additional reasons why having a JavaScript class presents maintainability issues on a huge codebase:
- Once a class is created, it is easy to extend it in a way that can infringe Vue reactivity and best practices. - After a class is created, it can be extended in a way that can infringe Vue reactivity and best practices.
- A class adds a layer of abstraction, which makes the component API and its inner workings less clear. - A class adds a layer of abstraction, which makes the component API and its inner workings less clear.
- It makes it harder to test. Since the class is instantiated by the component data function, it is - It makes it harder to test. Because the class is instantiated by the component data function, it is
harder to 'manage' component and class separately. harder to 'manage' component and class separately.
- Adding OOP to a functional codebase adds yet another way of writing code, reducing consistency and clarity. - Adding Object Oriented Principles (OOP) to a functional codebase adds yet another way of writing code, reducing consistency and clarity.
## Style guide ## Style guide
...@@ -234,8 +234,8 @@ for guidelines and best practices for testing your Vue components. ...@@ -234,8 +234,8 @@ for guidelines and best practices for testing your Vue components.
Each Vue component has a unique output. This output is always present in the render function. Each Vue component has a unique output. This output is always present in the render function.
Although we can test each method of a Vue component individually, our goal must be to test the output Although each method of a Vue component can be tested individually, our goal is to test the output
of the render/template function, which represents the state at all times. of the render function, which represents the state at all times.
Here's an example of a well structured unit test for [this Vue component](#appendix---vue-component-subject-under-test): Here's an example of a well structured unit test for [this Vue component](#appendix---vue-component-subject-under-test):
...@@ -366,7 +366,7 @@ component under test, with the `computed` property, for example). Remember to us ...@@ -366,7 +366,7 @@ component under test, with the `computed` property, for example). Remember to us
### Events ### Events
We should test for events emitted in response to an action within our component, this is useful to We should test for events emitted in response to an action in our component. This is used to
verify the correct events are being fired with the correct arguments. verify the correct events are being fired with the correct arguments.
For any DOM events we should use [`trigger`](https://vue-test-utils.vuejs.org/api/wrapper/#trigger) For any DOM events we should use [`trigger`](https://vue-test-utils.vuejs.org/api/wrapper/#trigger)
...@@ -416,7 +416,7 @@ You should only apply to be a Vue.js expert when your own merge requests and you ...@@ -416,7 +416,7 @@ You should only apply to be a Vue.js expert when your own merge requests and you
> This section is added temporarily to support the efforts to migrate the codebase from Vue 2.x to Vue 3.x > This section is added temporarily to support the efforts to migrate the codebase from Vue 2.x to Vue 3.x
Currently, we recommend to minimize adding certain features to the codebase to prevent increasing We recommend to minimize adding certain features to the codebase to prevent increasing
the tech debt for the eventual migration: the tech debt for the eventual migration:
- filters; - filters;
......
...@@ -26,7 +26,7 @@ Component's computed properties / methods or external helpers. ...@@ -26,7 +26,7 @@ Component's computed properties / methods or external helpers.
**What to use instead** **What to use instead**
Vue docs recommend using [mitt](https://github.com/developit/mitt) library. It's relatively small (200 bytes gzipped) and has a simple API: Vue documentation recommends using the [mitt](https://github.com/developit/mitt) library. It's relatively small (200 bytes gzipped) and has a clear API:
```javascript ```javascript
import mitt from 'mitt' import mitt from 'mitt'
...@@ -51,9 +51,9 @@ emitter.off('foo', onFoo) // unlisten ...@@ -51,9 +51,9 @@ emitter.off('foo', onFoo) // unlisten
**Event hub factory** **Event hub factory**
To make it easier for you to migrate existing event hubs to the new recommended approach, or simply We have created a factory that you can use to instantiate a new mitt-based event hub.
to create new ones, we have created a factory that you can use to instantiate a new mitt-based This makes it easier to migrate existing event hubs to the new recommended approach, or
event hub. to create new ones.
```javascript ```javascript
import createEventHub from '~/helpers/event_hub_factory'; import createEventHub from '~/helpers/event_hub_factory';
...@@ -88,7 +88,7 @@ It is not recommended to replace stateful components with functional components ...@@ -88,7 +88,7 @@ It is not recommended to replace stateful components with functional components
**Why?** **Why?**
In Vue 2.6 `slot` attribute was already deprecated in favor of `v-slot` directive but its usage is still allowed and sometimes we prefer using them because it simplifies unit tests (with old syntax, slots are rendered on `shallowMount`). However, in Vue 3 we can't use old syntax anymore. In Vue 2.6 `slot` attribute was already deprecated in favor of `v-slot` directive. The `slot` attribute usage is still allowed and sometimes we prefer using it because it simplifies unit tests (with old syntax, slots are rendered on `shallowMount`). However, in Vue 3 we can't use old syntax anymore.
**What to use instead** **What to use instead**
......
...@@ -146,7 +146,7 @@ The only way to change state in a Vuex store is by committing a mutation. ...@@ -146,7 +146,7 @@ The only way to change state in a Vuex store is by committing a mutation.
Most mutations are committed from an action using `commit`. If you don't have any Most mutations are committed from an action using `commit`. If you don't have any
asynchronous operations, you can call mutations from a component using the `mapMutations` helper. asynchronous operations, you can call mutations from a component using the `mapMutations` helper.
See the Vuex docs for examples of [committing mutations from components](https://vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components). See the Vuex documentation for examples of [committing mutations from components](https://vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components).
#### Naming Pattern: `REQUEST` and `RECEIVE` namespaces #### Naming Pattern: `REQUEST` and `RECEIVE` namespaces
...@@ -271,7 +271,7 @@ import { mapGetters } from 'vuex'; ...@@ -271,7 +271,7 @@ import { mapGetters } from 'vuex';
### `mutation_types.js` ### `mutation_types.js`
From [vuex mutations docs](https://vuex.vuejs.org/guide/mutations.html): From [Vuex mutations documentation](https://vuex.vuejs.org/guide/mutations.html):
> It is a commonly seen pattern to use constants for mutation types in various Flux implementations. > It is a commonly seen pattern to use constants for mutation types in various Flux implementations.
> This allows the code to take advantage of tooling like linters, and putting all constants in a > This allows the code to take advantage of tooling like linters, and putting all constants in a
> single file allows your collaborators to get an at-a-glance view of what mutations are possible > single file allows your collaborators to get an at-a-glance view of what mutations are possible
...@@ -429,7 +429,7 @@ export default { ...@@ -429,7 +429,7 @@ export default {
#### Testing Vuex concerns #### Testing Vuex concerns
Refer to [Vuex docs](https://vuex.vuejs.org/guide/testing.html) regarding testing Actions, Getters and Mutations. Refer to [Vuex documentation](https://vuex.vuejs.org/guide/testing.html) regarding testing Actions, Getters and Mutations.
#### Testing components that need a store #### Testing components that need a store
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment