diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
commit | d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch) | |
tree | 2341ef426af70ad1e289c38036737e04b0aa5007 /doc/development/fe_guide | |
parent | d6e514dd13db8947884cd58fe2a9c2a063400a9b (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.md | 2 | ||||
-rw-r--r-- | doc/development/fe_guide/content_editor.md | 20 | ||||
-rw-r--r-- | doc/development/fe_guide/droplab/droplab.md | 281 | ||||
-rw-r--r-- | doc/development/fe_guide/droplab/plugins/ajax.md | 46 | ||||
-rw-r--r-- | doc/development/fe_guide/droplab/plugins/filter.md | 55 | ||||
-rw-r--r-- | doc/development/fe_guide/droplab/plugins/index.md | 14 | ||||
-rw-r--r-- | doc/development/fe_guide/droplab/plugins/input_setter.md | 72 | ||||
-rw-r--r-- | doc/development/fe_guide/editor_lite.md | 9 | ||||
-rw-r--r-- | doc/development/fe_guide/haml.md | 15 | ||||
-rw-r--r-- | doc/development/fe_guide/index.md | 8 | ||||
-rw-r--r-- | doc/development/fe_guide/logging.md | 86 | ||||
-rw-r--r-- | doc/development/fe_guide/storybook.md | 16 | ||||
-rw-r--r-- | doc/development/fe_guide/style/scss.md | 2 |
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? |