Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
commitd9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch)
tree2341ef426af70ad1e289c38036737e04b0aa5007 /doc/development/fe_guide
parentd6e514dd13db8947884cd58fe2a9c2a063400a9b (diff)
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'doc/development/fe_guide')
-rw-r--r--doc/development/fe_guide/accessibility.md2
-rw-r--r--doc/development/fe_guide/content_editor.md20
-rw-r--r--doc/development/fe_guide/droplab/droplab.md281
-rw-r--r--doc/development/fe_guide/droplab/plugins/ajax.md46
-rw-r--r--doc/development/fe_guide/droplab/plugins/filter.md55
-rw-r--r--doc/development/fe_guide/droplab/plugins/index.md14
-rw-r--r--doc/development/fe_guide/droplab/plugins/input_setter.md72
-rw-r--r--doc/development/fe_guide/editor_lite.md9
-rw-r--r--doc/development/fe_guide/haml.md15
-rw-r--r--doc/development/fe_guide/index.md8
-rw-r--r--doc/development/fe_guide/logging.md86
-rw-r--r--doc/development/fe_guide/storybook.md16
-rw-r--r--doc/development/fe_guide/style/scss.md2
13 files changed, 129 insertions, 497 deletions
diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md
index 7c870de9a6c..c4ebef4c289 100644
--- a/doc/development/fe_guide/accessibility.md
+++ b/doc/development/fe_guide/accessibility.md
@@ -334,7 +334,7 @@ Keep in mind that:
- When you add `:hover` styles, in most cases you should add `:focus` styles too so that the styling is applied for both mouse **and** keyboard users.
- If you remove an interactive element's `outline`, make sure you maintain visual focus state in another way such as with `box-shadow`.
-See the [Pajamas Keyboard-only page](https://design.gitlab.com/accessibility-audits/keyboard-only) for more detail.
+See the [Pajamas Keyboard-only page](https://design.gitlab.com/accessibility-audits/keyboard-only/) for more detail.
## Tabindex
diff --git a/doc/development/fe_guide/content_editor.md b/doc/development/fe_guide/content_editor.md
index 956e7d0d56e..139825655e9 100644
--- a/doc/development/fe_guide/content_editor.md
+++ b/doc/development/fe_guide/content_editor.md
@@ -11,7 +11,7 @@ experience for [GitLab Flavored Markdown](../../user/markdown.md) in the GitLab
It also serves as the foundation for implementing Markdown-focused editors
that target other engines, like static site generators.
-We use [tiptap 2.0](https://www.tiptap.dev/) and [ProseMirror](https://prosemirror.net/)
+We use [tiptap 2.0](https://tiptap.dev/) and [ProseMirror](https://prosemirror.net/)
to build the Content Editor. These frameworks provide a level of abstraction on top of
the native
[`contenteditable`](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content) web technology.
@@ -143,7 +143,7 @@ The Content Editor is composed of three main layers:
### Editing tools UI
The editing tools UI are Vue components that display the editor's state and
-dispatch [commands](https://www.tiptap.dev/api/commands/#commands) to mutate it.
+dispatch [commands](https://tiptap.dev/api/commands/#commands) to mutate it.
They are located in the `~/content_editor/components` directory. For example,
the **Bold** toolbar button displays the editor's state by becoming active when
the user selects bold text. This button also dispatches the `toggleBold` command
@@ -159,7 +159,7 @@ sequenceDiagram
#### Node views
-We implement [node views](https://www.tiptap.dev/guide/node-views/vue/#node-views-with-vue)
+We implement [node views](https://tiptap.dev/guide/node-views/vue/#node-views-with-vue)
to provide inline editing tools for some content types, like tables and images. Node views
allow separating the presentation of a content type from its
[model](https://prosemirror.net/docs/guide/#doc.data_structures). Using a Vue component in
@@ -209,7 +209,7 @@ the following events:
- `blur`
- `error`.
-Learn more about these events in [Tiptap's event guide](https://www.tiptap.dev/api/events/).
+Learn more about these events in [Tiptap's event guide](https://tiptap.dev/api/events/).
```html
<script>
@@ -246,7 +246,7 @@ export default {
### The Tiptap editor object
-The Tiptap [Editor](https://www.tiptap.dev/api/editor) class manages
+The Tiptap [Editor](https://tiptap.dev/api/editor) class manages
the editor's state and encapsulates all the business logic that powers
the Content Editor. The Content Editor constructs a new instance of this class and
provides all the necessary extensions to support
@@ -255,9 +255,9 @@ provides all the necessary extensions to support
#### Implement new extensions
Extensions are the building blocks of the Content Editor. You can learn how to implement
-new ones by reading [Tiptap's guide](https://www.tiptap.dev/guide/custom-extensions).
-We recommend checking the list of built-in [nodes](https://www.tiptap.dev/api/nodes) and
-[marks](https://www.tiptap.dev/api/marks) before implementing a new extension
+new ones by reading [Tiptap's guide](https://tiptap.dev/guide/custom-extensions).
+We recommend checking the list of built-in [nodes](https://tiptap.dev/api/nodes) and
+[marks](https://tiptap.dev/api/marks) before implementing a new extension
from scratch.
Store the Content Editor extensions in the `~/content_editor/extensions` directory.
@@ -326,8 +326,8 @@ sequenceDiagram
```
Deserializers live in the extension modules. Read Tiptap's
-[parseHTML](https://www.tiptap.dev/guide/custom-extensions#parse-html) and
-[addAttributes](https://www.tiptap.dev/guide/custom-extensions#attributes) documentation to
+[parseHTML](https://tiptap.dev/guide/custom-extensions#parse-html) and
+[addAttributes](https://tiptap.dev/guide/custom-extensions#attributes) documentation to
learn how to implement them. Titap's API is a wrapper around ProseMirror's
[schema spec API](https://prosemirror.net/docs/ref/#model.SchemaSpec).
diff --git a/doc/development/fe_guide/droplab/droplab.md b/doc/development/fe_guide/droplab/droplab.md
deleted file mode 100644
index 8f1ecc115fe..00000000000
--- a/doc/development/fe_guide/droplab/droplab.md
+++ /dev/null
@@ -1,281 +0,0 @@
----
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
----
-
-# DropLab
-
-A generic dropdown for all of your custom dropdown needs.
-
-## Usage
-
-DropLab can be used by adding a `data-dropdown-trigger` HTML attribute. This
-attribute allows us to find the "trigger" _(toggle)_ for the dropdown, whether
-it's a button, link or input.
-
-The value of the `data-dropdown-trigger` should be a CSS selector that DropLab
-can use to find the trigger's dropdown list.
-
-You should also add the `data-dropdown` attribute to declare the dropdown list.
-The value is irrelevant.
-
-The DropLab class has no side effects, so you must always call `.init` when the
-DOM is ready. `DropLab.prototype.init` takes the same arguments as `DropLab.prototype.addHook`.
-If you don't provide any arguments, it globally queries and instantiates all
-DropLab-compatible dropdowns.
-
-```html
-<a href="#" data-dropdown-trigger="#list">Toggle</a>
-
-<ul id="list" data-dropdown>
- <!-- ... -->
-<ul>
-```
-
-```javascript
-const droplab = new DropLab();
-droplab.init();
-```
-
-As noted, we have a "Toggle" link that's declared as a trigger. It provides a
-selector to find the dropdown list it should control.
-
-### Static data
-
-You can add static list items.
-
-```html
-<a href="#" data-dropdown-trigger="#list">Toggle</a>
-
-<ul id="list" data-dropdown>
- <li>Static value 1</li>
- <li>Static value 2</li>
-<ul>
-```
-
-```javascript
-const droplab = new DropLab();
-droplab.init();
-```
-
-### Explicit instantiation
-
-You can pass the trigger and list elements as constructor arguments to return a
-non-global instance of DropLab using the `DropLab.prototype.init` method.
-
-```html
-<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>
-
-<ul id="list" data-dropdown>
- <!-- ... -->
-<ul>
-```
-
-```javascript
-const trigger = document.getElementById('trigger');
-const list = document.getElementById('list');
-
-const droplab = new DropLab();
-droplab.init(trigger, list);
-```
-
-You can also add hooks to an existing DropLab instance using `DropLab.prototype.addHook`.
-
-```html
-<a href="#" data-dropdown-trigger="#auto-dropdown">Toggle</a>
-<ul id="auto-dropdown" data-dropdown><!-- ... --><ul>
-
-<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>
-<ul id="list" data-dropdown><!-- ... --><ul>
-```
-
-```javascript
-const droplab = new DropLab();
-
-droplab.init();
-
-const trigger = document.getElementById('trigger');
-const list = document.getElementById('list');
-
-droplab.addHook(trigger, list);
-```
-
-### Dynamic data
-
-Adding `data-dynamic` to your dropdown element enables dynamic list
-rendering.
-
-You can template a list item using the keys of the data object provided. Use the
-handlebars syntax `{{ value }}` to HTML escape the value. Use the `<%= value %>`
-syntax to interpolate the value. Use the `<%= value %>` syntax to evaluate the
-value.
-
-Passing an array of objects to `DropLab.prototype.addData` renders that data
-for all `data-dynamic` dropdown lists tracked by that DropLab instance.
-
-```html
-<a href="#" data-dropdown-trigger="#list">Toggle</a>
-
-<ul id="list" data-dropdown data-dynamic>
- <li><a href="#" data-id="{{id}}">{{text}}</a></li>
-</ul>
-```
-
-```javascript
-const droplab = new DropLab();
-
-droplab.init().addData([{
- id: 0,
- text: 'Jacob',
-}, {
- id: 1,
- text: 'Jeff',
-}]);
-```
-
-Alternatively, you can specify a specific dropdown to add this data to by
-passing the data as the second argument and the `id` of the trigger element as
-the first argument.
-
-```html
-<a href="#" data-dropdown-trigger="#list" id="trigger">Toggle</a>
-
-<ul id="list" data-dropdown data-dynamic>
- <li><a href="#" data-id="{{id}}">{{text}}</a></li>
-</ul>
-```
-
-```javascript
-const droplab = new DropLab();
-
-droplab.init().addData('trigger', [{
- id: 0,
- text: 'Jacob',
-}, {
- id: 1,
- text: 'Jeff',
-}]);
-```
-
-This allows you to mix static and dynamic content, even with one trigger.
-
-Note the use of scoping regarding the `data-dropdown` attribute to capture both
-dropdown lists, one of which is dynamic.
-
-```html
-<input id="trigger" data-dropdown-trigger="#list">
-<div id="list" data-dropdown>
- <ul>
- <li><a href="#">Static item 1</a></li>
- <li><a href="#">Static item 2</a></li>
- </ul>
- <ul data-dynamic>
- <li><a href="#" data-id="{{id}}">{{text}}</a></li>
- </ul>
-</div>
-```
-
-```javascript
-const droplab = new DropLab();
-
-droplab.init().addData('trigger', [{
- id: 0,
- text: 'Jacob',
-}, {
- id: 1,
- text: 'Jeff',
-}]);
-```
-
-## Internal selectors
-
-DropLab adds some CSS classes to help lower the barrier to integration.
-
-For example:
-
-- The `droplab-item-selected` CSS class is added to items that have been
- selected either by a mouse click or by enter key selection.
-- The `droplab-item-active` CSS class is added to items that have been selected
- using arrow key navigation.
-- You can add the `droplab-item-ignore` CSS class to any item that you don't
- want to be selectable. For example, an `<li class="divider"></li>` list
- divider element that shouldn't be interactive.
-
-## Internal events
-
-DropLab uses some custom events to help lower the barrier to integration.
-
-For example:
-
-- The `click.dl` event is fired when an `li` list item has been clicked. It's
- also fired when a list item has been selected with the keyboard. It's also
- fired when a `HookButton` button is clicked (a registered `button` tag or `a`
- tag trigger).
-- The `input.dl` event is fired when a `HookInput` (a registered `input` tag
- trigger) triggers an `input` event.
-- The `mousedown.dl` event is fired when a `HookInput` triggers a `mousedown`
- event.
-- The `keyup.dl` event is fired when a `HookInput` triggers a `keyup` event.
-- The `keydown.dl` event is fired when a `HookInput` triggers a `keydown` event.
-
-These custom events add a `detail` object to the vanilla `Event` object that
-provides some potentially useful data.
-
-## Plugins
-
-Plugins are objects that are registered to be executed when a hook is added (when
-a DropLab trigger and dropdown are instantiated).
-
-If no modules API is detected, the library falls back as it does with
-`window.DropLab` and adds `window.DropLab.plugins.PluginName`.
-
-### Usage
-
-To use plugins, you can pass them in an array as the third argument of
-`DropLab.prototype.init` or `DropLab.prototype.addHook`. Some plugins require
-configuration values; the configuration object can be passed as the fourth argument.
-
-```html
-<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>
-<ul id="list" data-dropdown><!-- ... --><ul>
-```
-
-```javascript
-const droplab = new DropLab();
-
-const trigger = document.getElementById('trigger');
-const list = document.getElementById('list');
-
-droplab.init(trigger, list, [droplabAjax], {
- droplabAjax: {
- endpoint: '/some-endpoint',
- method: 'setData',
- },
-});
-```
-
-### Documentation
-
-Refer to the list of available [DropLab plugins](plugins/index.md) for
-information about their use.
-
-### Development
-
-When plugins are initialised for a DropLab trigger+dropdown, DropLab calls the
-plugins' `init` function, so this must be implemented in the plugin.
-
-```javascript
-class MyPlugin {
- static init() {
- this.someProp = 'someProp';
- this.someMethod();
- }
-
- static someMethod() {
- this.otherProp = 'otherProp';
- }
-}
-
-export default MyPlugin;
-```
diff --git a/doc/development/fe_guide/droplab/plugins/ajax.md b/doc/development/fe_guide/droplab/plugins/ajax.md
deleted file mode 100644
index f12f8f260c7..00000000000
--- a/doc/development/fe_guide/droplab/plugins/ajax.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
----
-
-# Ajax plugin
-
-`Ajax` is a DropLab plugin that allows for retrieving and rendering list data
-from a server.
-
-## Usage
-
-Add the `Ajax` object to the plugins array of a `DropLab.prototype.init` or
-`DropLab.prototype.addHook` call.
-
-`Ajax` requires 2 configuration values: the `endpoint` and `method`.
-
-- `endpoint`: Should be a URL to the request endpoint.
-- `method`: Should be `setData` or `addData`.
-- `setData`: Completely replaces the dropdown with the response data.
-- `addData`: Appends the response data to the current dropdown list.
-
-```html
-<a href="#" id="trigger" data-dropdown-trigger="#list">Toggle</a>
-<ul id="list" data-dropdown><!-- ... --><ul>
-```
-
-```javascript
-const droplab = new DropLab();
-
-const trigger = document.getElementById('trigger');
-const list = document.getElementById('list');
-
-droplab.addHook(trigger, list, [Ajax], {
- Ajax: {
- endpoint: '/some-endpoint',
- method: 'setData',
- },
-});
-```
-
-Optionally, you can set `loadingTemplate` to a HTML string. This HTML string
-replaces the dropdown list while the request is pending.
-
-Additionally, you can set `onError` to a function to catch any XHR errors.
diff --git a/doc/development/fe_guide/droplab/plugins/filter.md b/doc/development/fe_guide/droplab/plugins/filter.md
deleted file mode 100644
index 79f10cdb6c1..00000000000
--- a/doc/development/fe_guide/droplab/plugins/filter.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
----
-
-# Filter plugin
-
-`Filter` is a DropLab plugin that allows for filtering data that has been added
-to the dropdown using a simple fuzzy string search of an input value.
-
-## Usage
-
-Add the `Filter` object to the plugins array of a `DropLab.prototype.init` or
-`DropLab.prototype.addHook` call.
-
-- `Filter`: Requires a configuration value for `template`.
-- `template`: Should be the key of the objects within your data array that you
- want to compare to the user input string, for filtering.
-
-```html
-<input href="#" id="trigger" data-dropdown-trigger="#list">
-<ul id="list" data-dropdown data-dynamic>
- <li><a href="#" data-id="{{id}}">{{text}}</a></li>
-<ul>
-```
-
-```javascript
-const droplab = new DropLab();
-
-const trigger = document.getElementById('trigger');
-const list = document.getElementById('list');
-
-droplab.init(trigger, list, [Filter], {
- Filter: {
- template: 'text',
- },
-});
-
-droplab.addData('trigger', [{
- id: 0,
- text: 'Jacob',
-}, {
- id: 1,
- text: 'Jeff',
-}]);
-```
-
-In the previous code, the input string is compared against the `test` key of the
-passed data objects.
-
-Optionally you can set `filterFunction` to a function. This function is then
-used instead of `Filter`'s built-in string search. `filterFunction` is passed
-two arguments: the first is one of the data objects, and the second is the
-current input value.
diff --git a/doc/development/fe_guide/droplab/plugins/index.md b/doc/development/fe_guide/droplab/plugins/index.md
deleted file mode 100644
index c7a2865ca83..00000000000
--- a/doc/development/fe_guide/droplab/plugins/index.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-description: A list of DropLab plugins.
----
-
-# DropLab plugins
-
-The following plugins are available for use with [DropLab](../droplab.md):
-
-- [Ajax plugin](ajax.md)
-- [Filter plugin](filter.md)
-- [InputSetter plugin](input_setter.md)
diff --git a/doc/development/fe_guide/droplab/plugins/input_setter.md b/doc/development/fe_guide/droplab/plugins/input_setter.md
deleted file mode 100644
index a3c073520cb..00000000000
--- a/doc/development/fe_guide/droplab/plugins/input_setter.md
+++ /dev/null
@@ -1,72 +0,0 @@
----
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
----
-
-# InputSetter plugin
-
-`InputSetter` is a DropLab plugin that allows for updating DOM out of the scope
-of DropLab when a list item is clicked.
-
-## Usage
-
-Add the `InputSetter` object to the plugins array of a `DropLab.prototype.init`
-or `DropLab.prototype.addHook` call.
-
-- `InputSetter`: Requires a configuration value for `input` and `valueAttribute`.
-- `input`: The DOM element that you want to manipulate.
-- `valueAttribute`: A string that's the name of an attribute on your list items
- that's used to get the value to update the `input` element with.
-
-You can also set the `InputSetter` configuration to an array of objects, which
-allows you to update multiple elements.
-
-```html
-<input id="input" value="">
-<div id="div" data-selected-id=""></div>
-
-<input href="#" id="trigger" data-dropdown-trigger="#list">
-<ul id="list" data-dropdown data-dynamic>
- <li><a href="#" data-id="{{id}}">{{text}}</a></li>
-<ul>
-```
-
-```javascript
-const droplab = new DropLab();
-
-const trigger = document.getElementById('trigger');
-const list = document.getElementById('list');
-
-const input = document.getElementById('input');
-const div = document.getElementById('div');
-
-droplab.init(trigger, list, [InputSetter], {
- InputSetter: [{
- input: input,
- valueAttribute: 'data-id',
- } {
- input: div,
- valueAttribute: 'data-id',
- inputAttribute: 'data-selected-id',
- }],
-});
-
-droplab.addData('trigger', [{
- id: 0,
- text: 'Jacob',
-}, {
- id: 1,
- text: 'Jeff',
-}]);
-```
-
-In the previous code, if the second list item was clicked, it would update the
-`#input` element to have a `value` of `1`, it would also update the `#div`
-element's `data-selected-id` to `1`.
-
-Optionally, you can set `inputAttribute` to a string that's the name of an
-attribute on your `input` element that you want to update. If you don't provide
-an `inputAttribute`, `InputSetter` updates the `value` of the `input`
-element if it's an `INPUT` element, or the `textContent` of the `input` element
-if it isn't an `INPUT` element.
diff --git a/doc/development/fe_guide/editor_lite.md b/doc/development/fe_guide/editor_lite.md
deleted file mode 100644
index 5020bf9eeeb..00000000000
--- a/doc/development/fe_guide/editor_lite.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'source_editor.md'
-remove_date: '2021-09-19'
----
-
-This document was moved to [another location](source_editor.md).
-
-<!-- This redirect file can be deleted after <2021-09-19>. -->
-<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/fe_guide/haml.md b/doc/development/fe_guide/haml.md
index 8f501007755..f905fdad77e 100644
--- a/doc/development/fe_guide/haml.md
+++ b/doc/development/fe_guide/haml.md
@@ -57,7 +57,7 @@ For example:
When using the GitLab UI form builder, the following components are available for use in HAML.
NOTE:
-Currently only `gitlab_ui_checkbox_component` is available but more components are planned.
+Currently only the listed components are available but more components are planned.
#### gitlab_ui_checkbox_component
@@ -72,3 +72,16 @@ Currently only `gitlab_ui_checkbox_component` is available but more components a
| `checked_value` | Value when checkbox is checked. | `String` | `false` (`'1'`) |
| `unchecked_value` | Value when checkbox is unchecked. | `String` | `false` (`'0'`) |
| `label_options` | Options that are passed to [Rails `label` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label). | `Hash` | `false` (`{}`) |
+
+#### gitlab_ui_radio_component
+
+[GitLab UI Docs](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-form-form-radio--default)
+
+| Argument | Description | Type | Required (default value) |
+|---|---|---|---|
+| `method` | Attribute on the object passed to `gitlab_ui_form_for`. | `Symbol` | `true` |
+| `value` | The value of the radio tag. | `Symbol` | `true` |
+| `label` | Radio label. | `String` | `true` |
+| `help_text` | Help text displayed below the radio button. | `String` | `false` (`nil`) |
+| `radio_options` | Options that are passed to [Rails `radio_button` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-radio_button). | `Hash` | `false` (`{}`) |
+| `label_options` | Options that are passed to [Rails `label` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label). | `Hash` | `false` (`{}`) |
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
index a6b49394733..9ef4375d795 100644
--- a/doc/development/fe_guide/index.md
+++ b/doc/development/fe_guide/index.md
@@ -133,9 +133,13 @@ Best practices for monitoring and maximizing frontend performance.
Frontend security practices.
-## [Accessibility](accessibility.md)
+## Accessibility
-Our accessibility standards and resources.
+Our [accessibility standards and resources](accessibility.md).
+
+## Logging
+
+Best practices for [client-side logging](logging.md) for GitLab frontend development.
## [Internationalization (i18n) and Translations](../i18n/externalization.md)
diff --git a/doc/development/fe_guide/logging.md b/doc/development/fe_guide/logging.md
new file mode 100644
index 00000000000..26633eade43
--- /dev/null
+++ b/doc/development/fe_guide/logging.md
@@ -0,0 +1,86 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Client-side logging for frontend development
+
+This guide contains the best practices for client-side logging for GitLab
+frontend development.
+
+## When to log to the browser console
+
+We do not want to log unnecessarily to the browser console, as excessively
+noisy console logs are not easy to read, parse, or process. We **do** want to
+give visibility to unintended events in the system. If a possible but unexpected
+exception occurs during runtime, we want to log the details of this exception.
+These logs can give significantly helpful context to end users creating issues, or
+contributors diagnosing problems.
+
+Whenever a `catch(e)` exists, and `e` is something unexpected, log the details.
+
+### What makes an error unexpected?
+
+Sometimes a caught exception can be part of normal operations. For instance, third-party
+libraries might throw an exception based on certain inputs. If we can gracefully
+handle these exceptions, then they are expected. Don't log them noisily.
+For example:
+
+```javascript
+try {
+ // Here, we call a method based on some user input.
+ // `doAThing` will throw an exception if the input is invalid.
+ const userInput = getUserInput();
+ doAThing(userInput);
+} catch (e) {
+ if (e instanceof FooSyntaxError) {
+ // To handle a `FooSyntaxError`, we just need to instruct the user to change their input.
+ // This isn't unexpected, and is part of normal operations.
+ setUserMessage(`Try writing better code. ${e.message}`);
+ } else {
+ // We're not sure what `e` is, so something unexpected and bad happened...
+ logError(e);
+ setUserMessage('Something unexpected happened...');
+ }
+}
+```
+
+## How to log an error
+
+We have a helpful `~/lib/logger` module which encapsulates how we can
+consistently log runtime errors in GitLab. Import `logError` from this
+module, and use it as you normally would `console.error`. Pass the actual `Error`
+object, so the stack trace and other details can be captured in the log:
+
+```javascript
+// 1. Import the logger module.
+import { logError } from '~/lib/logger';
+
+export const doThing = () => {
+ return foo()
+ .then(() => {
+ // ...
+ })
+ .catch(e => {
+ // 2. Use `logError` like you would `console.error`.
+ logError('An unexpected error occurred while doing the thing', e);
+
+ // We may or may not want to present that something bad happened to the end user.
+ showThingFailed();
+ });
+};
+```
+
+## Relation to frontend observability
+
+Client-side logging is strongly related to
+[Frontend observability](https://about.gitlab.com/company/team/structure/working-groups/frontend-observability/).
+We want unexpected errors to be observed by our monitoring systems, so
+we can quickly react to user-facing issues. For a number of reasons, it is
+unfeasible to send every log to the monitoring system. Don't shy away from using
+`~/lib/logger`, but consider controlling which messages passed to `~/lib/logger`
+are actually sent to the monitoring systems.
+
+A cohesive logging module helps us control these side effects consistently
+across the various entry points.
diff --git a/doc/development/fe_guide/storybook.md b/doc/development/fe_guide/storybook.md
index 15225cc1deb..a46157d2cad 100644
--- a/doc/development/fe_guide/storybook.md
+++ b/doc/development/fe_guide/storybook.md
@@ -33,19 +33,25 @@ Stories can be added for any Vue component in the `gitlab` repository.
To add a story:
1. Create a new `.stories.js` file in the same directory as the Vue component.
- The file name should have the same prefix as the Vue component.
+ The filename should have the same prefix as the Vue component.
```txt
vue_shared/
├─ components/
│ ├─ sidebar
- │ │ ├─ todo_button.vue
- │ │ ├─ todo_button.stories.js
+ │ | ├─ todo_toggle
+ │ | | ├─ todo_button.vue
+ │ │ | ├─ todo_button.stories.js
```
1. Write the story as per the [official Storybook instructions](https://storybook.js.org/docs/vue/writing-stories/introduction/)
Notes:
- Specify the `title` field of the story as the component's file path from the `javascripts/` directory,
- e.g. if the component is located at `app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue`, specify the `title` as
- `vue_shared/components/To-do Button`. This will ensure the Storybook navigation maps closely to our internal directory structure.
+ e.g. if the component is located at `app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue`, specify the story `title` as `vue_shared/components/sidebar/todo_toggle/todo_button`. This will ensure the Storybook navigation maps closely to our internal directory structure.
+
+## Mock backend APIs
+
+GitLab’s Storybook uses [MirajeJS](https://miragejs.com/) to mock REST and GraphQL APIs. Storybook shares the MirajeJS server
+with the [frontend integration tests](../testing_guide/testing_levels.md#frontend-integration-tests). You can find the MirajeJS
+configuration files in `spec/frontend_integration/mock_server`.
diff --git a/doc/development/fe_guide/style/scss.md b/doc/development/fe_guide/style/scss.md
index 6d9bbdd3f2d..ffaaa3e87c7 100644
--- a/doc/development/fe_guide/style/scss.md
+++ b/doc/development/fe_guide/style/scss.md
@@ -45,7 +45,7 @@ result (such as `ml-1` becoming `gl-ml-2`).
If a class you need has not been added to GitLab UI, you get to add it! Follow the naming patterns documented in the [utility files](https://gitlab.com/gitlab-org/gitlab-ui/-/tree/main/src/scss/utility-mixins) and refer to [GitLab UI's CSS documentation](https://gitlab.com/gitlab-org/gitlab-ui/-/blob/main/doc/contributing/adding_css.md#adding-utility-mixins) for more details, especially about adding responsive and stateful rules.
-If it is not possible to wait for a GitLab UI update (generally one day), add the class to [`utilities.scss`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/stylesheets/utilities.scss) following the same naming conventions documented in GitLab UI. A follow—up issue to backport the class to GitLab UI and delete it from GitLab should be opened.
+If it is not possible to wait for a GitLab UI update (generally one day), add the class to [`utilities.scss`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/stylesheets/utilities.scss) following the same naming conventions documented in GitLab UI. A follow-up issue to backport the class to GitLab UI and delete it from GitLab should be opened.
#### When should I create component classes?