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-07-20 12:55:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-20 12:55:51 +0300
commite8d2c2579383897a1dd7f9debd359abe8ae8373d (patch)
treec42be41678c2586d49a75cabce89322082698334 /doc/development/fe_guide
parentfc845b37ec3a90aaa719975f607740c22ba6a113 (diff)
Add latest changes from gitlab-org/gitlab@14-1-stable-eev14.1.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.md6
-rw-r--r--doc/development/fe_guide/editor_lite.md265
-rw-r--r--doc/development/fe_guide/frontend_faq.md2
-rw-r--r--doc/development/fe_guide/graphql.md29
-rw-r--r--doc/development/fe_guide/index.md2
-rw-r--r--doc/development/fe_guide/performance.md18
-rw-r--r--doc/development/fe_guide/source_editor.md264
-rw-r--r--doc/development/fe_guide/storybook.md51
-rw-r--r--doc/development/fe_guide/style/html.md2
-rw-r--r--doc/development/fe_guide/style/scss.md4
-rw-r--r--doc/development/fe_guide/vue3_migration.md49
-rw-r--r--doc/development/fe_guide/vuex.md57
13 files changed, 467 insertions, 284 deletions
diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md
index 15818941b24..0cd7cf58b58 100644
--- a/doc/development/fe_guide/accessibility.md
+++ b/doc/development/fe_guide/accessibility.md
@@ -510,7 +510,7 @@ Proper research and testing should be done to ensure compliance with [WCAG](http
### Viewing the browser accessibility tree
- [Firefox DevTools guide](https://developer.mozilla.org/en-US/docs/Tools/Accessibility_inspector#accessing_the_accessibility_inspector)
-- [Chrome DevTools guide](https://developers.google.com/web/tools/chrome-devtools/accessibility/reference#pane)
+- [Chrome DevTools guide](https://developer.chrome.com/docs/devtools/accessibility/reference#pane)
### Browser extensions
diff --git a/doc/development/fe_guide/content_editor.md b/doc/development/fe_guide/content_editor.md
index f6329f39636..6cf4076bf83 100644
--- a/doc/development/fe_guide/content_editor.md
+++ b/doc/development/fe_guide/content_editor.md
@@ -54,7 +54,7 @@ the status of the ongoing development for CommonMark and GitLab Flavored Markdow
To include the Content Editor in your feature, import the `createContentEditor` factory
function and the `ContentEditor` Vue component. `createContentEditor` sets up an instance
-of [tiptap's Editor class](https://www.tiptap.dev/api/editor) with all the necessary
+of [tiptap's Editor class](https://www.tiptap.dev/api/editor/) with all the necessary
extensions to support editing GitLab Flavored Markdown content. It also creates
a Markdown serializer that allows exporting tiptap's document format to Markdown.
@@ -90,7 +90,9 @@ export default {
try {
await this.contentEditor.setSerializedContent(this.content);
} catch (e) {
- createFlash(__('There was an error loading content in the editor'), e);
+ createFlash({
+ message: __('There was an error loading content in the editor'), error: e
+ });
}
},
methods: {
diff --git a/doc/development/fe_guide/editor_lite.md b/doc/development/fe_guide/editor_lite.md
index f28588c23e9..5020bf9eeeb 100644
--- a/doc/development/fe_guide/editor_lite.md
+++ b/doc/development/fe_guide/editor_lite.md
@@ -1,264 +1,9 @@
---
-stage: Create
-group: Editor
-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
+redirect_to: 'source_editor.md'
+remove_date: '2021-09-19'
---
-# Editor Lite **(FREE)**
+This document was moved to [another location](source_editor.md).
-**Editor Lite** provides the editing experience at GitLab. This thin wrapper around
-[the Monaco editor](https://microsoft.github.io/monaco-editor/) provides necessary
-helpers and abstractions, and extends Monaco [using extensions](#extensions). Multiple
-GitLab features use it, including:
-
-- [Web IDE](../../user/project/web_ide/index.md)
-- [CI Linter](../../ci/lint.md)
-- [Snippets](../../user/snippets.md)
-- [Web Editor](../../user/project/repository/web_editor.md)
-- [Security Policies](../../user/application_security/threat_monitoring/index.md)
-
-## How to use Editor Lite
-
-Editor Lite is framework-agnostic and can be used in any application, including both
-Rails and Vue. To help with integration, we have the dedicated `<editor-lite>`
-Vue component, but the integration of Editor Lite is generally straightforward:
-
-1. Import Editor Lite:
-
- ```javascript
- import EditorLite from '~/editor/editor_lite';
- ```
-
-1. Initialize global editor for the view:
-
- ```javascript
- const editor = new EditorLite({
- // Editor Options.
- // The list of all accepted options can be found at
- // https://microsoft.github.io/monaco-editor/api/enums/monaco.editor.editoroption.html
- });
- ```
-
-1. Create an editor's instance:
-
- ```javascript
- editor.createInstance({
- // Editor Lite configuration options.
- })
- ```
-
-An instance of Editor Lite accepts the following configuration options:
-
-| Option | Required? | Description |
-| -------------- | ------- | ---- |
-| `el` | `true` | `HTML Node`: The element on which to render the editor. |
-| `blobPath` | `false` | `String`: The name of a file to render in the editor, used to identify the correct syntax highlighter to use with that file, or another file type. Can accept wildcards like `*.js` when the actual filename isn't known or doesn't play any role. |
-| `blobContent` | `false` | `String`: The initial content to render in the editor. |
-| `extensions` | `false` | `Array`: Extensions to use in this instance. |
-| `blobGlobalId` | `false` | `String`: An auto-generated property.<br>**Note:** This property may go away in the future. Do not pass `blobGlobalId` unless you know what you're doing.|
-| Editor Options | `false` | `Object(s)`: Any property outside of the list above is treated as an Editor Option for this particular instance. Use this field to override global Editor Options on the instance level. A full [index of Editor Options](https://microsoft.github.io/monaco-editor/api/enums/monaco.editor.editoroption.html) is available. |
-
-## API
-
-The editor uses the same public API as
-[provided by Monaco editor](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html)
-with additional functions on the instance level:
-
-| Function | Arguments | Description
-| --------------------- | ----- | ----- |
-| `updateModelLanguage` | `path`: String | Updates the instance's syntax highlighting to follow the extension of the passed `path`. Available only on the instance level.|
-| `use` | Array of objects | Array of extensions to apply to the instance. Accepts only the array of _objects_. You must fetch the extensions' ES6 modules must be fetched and resolved in your views or components before they are passed to `use`. This property is available on _instance_ (applies extension to this particular instance) and _global editor_ (applies the same extension to all instances) levels. |
-| Monaco Editor options | See [documentation](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html) | Default Monaco editor options |
-
-## Tips
-
-1. Editor's loading state.
-
- The loading state is built in to Editor Lite, 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 should contain the editor. When bootstrapping,
- Editor Lite shows the loader automatically.
-
- ![Editor Lite: loading state](img/editor_lite_loading.png)
-
-1. Update syntax highlighting if the filename changes.
-
- ```javascript
- // fileNameEl here is the HTML input element that contains the file name
- fileNameEl.addEventListener('change', () => {
- this.editor.updateModelLanguage(fileNameEl.value);
- });
- ```
-
-1. Get the editor's content.
-
- We may set up listeners on the editor for every change, but it rapidly can become
- an expensive operation. Instead, get the editor's content when it's needed.
- For example, on a form's submission:
-
- ```javascript
- form.addEventListener('submit', () => {
- my_content_variable = this.editor.getValue();
- });
- ```
-
-1. Performance
-
- Even though Editor Lite itself is extremely slim, it still depends on Monaco editor,
- which adds weight. Every time you add Editor Lite to a view, the JavaScript bundle's
- size significantly increases, affecting your view's loading performance. We recommend
- you import the editor on demand if either:
-
- - You're uncertain if the view needs the editor.
- - The editor is a secondary element of the view.
-
- Loading Editor Lite on demand is handled like loading any other module:
-
- ```javascript
- someActionFunction() {
- import(/* webpackChunkName: 'EditorLite' */ '~/editor/editor_lite').
- then(({ default: EditorLite }) => {
- const editor = new EditorLite();
- ...
- });
- ...
- }
- ```
-
-## Extensions
-
-Editor Lite provides a universal, extensible editing tool to the whole product,
-and doesn't depend on any particular group. Even though the Editor Lite's core is owned by
-[Create::Editor FE Team](https://about.gitlab.com/handbook/engineering/development/dev/create-editor/),
-any group can own the extensions—the main functional elements. The goal of
-Editor Lite extensions is to keep the editor's core slim and stable. Any
-needed features can be added as extensions to this core. Any group can
-build and own new editing features without worrying about changes to Editor Lite
-breaking or overriding them.
-
-You can depend on other modules in your extensions. This organization helps keep
-the size of Editor Lite's core at bay by importing dependencies only when needed.
-
-Structurally, the complete implementation of Editor Lite can be presented as this diagram:
-
-```mermaid
-graph TD;
- B[Extension 1]---A[Editor Lite]
- C[Extension 2]---A[Editor Lite]
- D[Extension 3]---A[Editor Lite]
- E[...]---A[Editor Lite]
- F[Extension N]---A[Editor Lite]
- A[Editor Lite]---Z[Monaco]
-```
-
-An extension is an ES6 module that exports a JavaScript object:
-
-```javascript
-import { Position } from 'monaco-editor';
-
-export default {
- navigateFileStart() {
- this.setPosition(new Position(1, 1));
- },
-};
-
-```
-
-In the extension's functions, `this` refers to the current Editor Lite instance.
-Using `this`, you get access to the complete instance's API, such as the
-`setPosition()` method in this particular case.
-
-### Using an existing extension
-
-Adding an extension to Editor Lite's instance requires the following steps:
-
-```javascript
-import EditorLite from '~/editor/editor_lite';
-import MyExtension from '~/my_extension';
-
-const editor = new EditorLite().createInstance({
- ...
-});
-editor.use(MyExtension);
-```
-
-### Creating an extension
-
-Let's create our first Editor Lite extension. Extensions are
-[ES6 modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/) exporting a
-basic `Object`, used to extend Editor Lite's features. As a test, let's
-create an extension that extends Editor Lite with a new function that, when called,
-outputs the editor's content in `alert`.
-
-`~/my_folder/my_fancy_extension.js:`
-
-```javascript
-export default {
- throwContentAtMe() {
- alert(this.getValue());
- },
-};
-```
-
-In the code example, `this` refers to the instance. By referring to the instance,
-we can access the complete underlying
-[Monaco editor API](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html),
-which includes functions like `getValue()`.
-
-Now let's use our extension:
-
-`~/my_folder/component_bundle.js`:
-
-```javascript
-import EditorLite from '~/editor/editor_lite';
-import MyFancyExtension from './my_fancy_extension';
-
-const editor = new EditorLite().createInstance({
- ...
-});
-editor.use(MyFancyExtension);
-...
-someButton.addEventListener('click', () => {
- editor.throwContentAtMe();
-});
-```
-
-First of all, we import Editor Lite and our new extension. Then we create the
-editor and its instance. By default Editor Lite has no `throwContentAtMe` method.
-But the `editor.use(MyFancyExtension)` line brings that method to our instance.
-After that, we can use it any time we need it. In this case, we call it when some
-theoretical button has been clicked.
-
-This script would result in an alert containing the editor's content when `someButton` is clicked.
-
-![Editor Lite new extension's result](img/editor_lite_create_ext.png)
-
-### Tips
-
-1. Performance
-
- Just like Editor Lite itself, any extension can be loaded on demand to not harm
- loading performance of the views:
-
- ```javascript
- const EditorPromise = import(
- /* webpackChunkName: 'EditorLite' */ '~/editor/editor_lite'
- );
- const MarkdownExtensionPromise = import('~/editor/editor_markdown_ext');
-
- Promise.all([EditorPromise, MarkdownExtensionPromise])
- .then(([{ default: EditorLite }, { default: MarkdownExtension }]) => {
- const editor = new EditorLite().createInstance({
- ...
- });
- editor.use(MarkdownExtension);
- });
- ```
-
-1. Using multiple extensions
-
- Just pass the array of extensions to your `use` method:
-
- ```javascript
- editor.use([FileTemplateExtension, MyFancyExtension]);
- ```
+<!-- 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/frontend_faq.md b/doc/development/fe_guide/frontend_faq.md
index 6b9d5ace4e6..1e8f7f5fb81 100644
--- a/doc/development/fe_guide/frontend_faq.md
+++ b/doc/development/fe_guide/frontend_faq.md
@@ -157,7 +157,7 @@ export const fetchFoos = ({ state }) => {
Sometimes it's necessary to test locally what the frontend production build would produce, to do so the steps are:
1. Stop webpack: `gdk stop webpack`.
-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 `gitlab/config` folder, scroll down to the `webpack` section, and change `dev_server` to `enabled: false`.
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
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 844ef2156d9..7fa9e957f56 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -906,6 +906,35 @@ apollo: {
},
```
+### Best Practices
+
+#### When to use (and not use) `update` hook in mutations
+
+Apollo Client's [`.mutate()`](https://www.apollographql.com/docs/react/api/core/ApolloClient/#ApolloClient.mutate)
+method exposes an `update` hook that is invoked twice during the mutation lifecycle:
+
+- Once at the beginning. That is, before the mutation has completed.
+- Once after the mutation has completed.
+
+You should use this hook only if you're adding or removing an item from the store
+(that is, ApolloCache). If you're _updating_ an existing item, it is usually represented by
+a global `id`.
+
+In that case, presence of this `id` in your mutation query definition makes the store update
+automatically. Here's an example of a typical mutation query with `id` present in it:
+
+```graphql
+mutation issueSetWeight($input: IssueSetWeightInput!) {
+ issuableSetWeight: issueSetWeight(input: $input) {
+ issuable: issue {
+ id
+ weight
+ }
+ errors
+ }
+}
+```
+
### Testing
#### Generating the GraphQL schema
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
index 00f0d72571a..325310ad05c 100644
--- a/doc/development/fe_guide/index.md
+++ b/doc/development/fe_guide/index.md
@@ -95,7 +95,7 @@ How we implement [keyboard shortcuts](keyboard_shortcuts.md) that can be customi
## Editors
-GitLab text editing experiences are provided by the [Source Editor](editor_lite.md) and
+GitLab text editing experiences are provided by the [Source Editor](source_editor.md) and
the [Content Editor](content_editor.md).
## Frontend FAQ
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index dd3945ae324..e7f347554d7 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -255,18 +255,18 @@ We support two types of prefetching for the chunks:
- The [`prefetch` link type](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/prefetch)
is used to prefetch a chunk for the future navigation
-- The [`preload` link type](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preloadh)
- is used to prefetch a chunk that is crucial for the current navigation but is not
+- The [`preload` link type](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload)
+ is used to prefetch a chunk that is crucial for the current navigation but is not
discovered until later in the rendering process
-Both `prefetch` and `preload` links bring the loading performance benefit to the pages. Both are
+Both `prefetch` and `preload` links bring the loading performance benefit to the pages. Both are
fetched asynchronously, but contrary to [deferring the loading](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer)
of the assets which is used for other JavaScript resources in the product by default, `prefetch` and
-`preload` neither parse nor execute the fetched script unless explicitly imported in any JavaScript
-module. This allows to cache the fetched resources without blocking the execution of the
+`preload` neither parse nor execute the fetched script unless explicitly imported in any JavaScript
+module. This allows to cache the fetched resources without blocking the execution of the
remaining page resources.
-To prefetch a JavaScript chunk in a HAML view, `:prefetch_asset_tags` with the combination of
+To prefetch a JavaScript chunk in a HAML view, `:prefetch_asset_tags` with the combination of
the `webpack_preload_asset_tag` helper is provided:
```javascript
@@ -280,8 +280,8 @@ This snippet will add a new `<link rel="preload">` element into the resulting HT
<link rel="preload" href="/assets/webpack/monaco.chunk.js" as="script" type="text/javascript">
```
-By default, `webpack_preload_asset_tag` will `preload` the chunk. You don't need to worry about
-`as` and `type` attributes for preloading the JavaScript chunks. However, when a chunk is not
+By default, `webpack_preload_asset_tag` will `preload` the chunk. You don't need to worry about
+`as` and `type` attributes for preloading the JavaScript chunks. However, when a chunk is not
critical, for the current navigation, one has to explicitly request `prefetch`:
```javascript
@@ -454,5 +454,5 @@ General tips:
- [WebPage Test](https://www.webpagetest.org) for testing site loading time and size.
- [Google PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/) grades web pages and provides feedback to improve the page.
-- [Profiling with Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/)
+- [Profiling with Chrome DevTools](https://developer.chrome.com/docs/devtools/)
- [Browser Diet](https://browserdiet.com/) is a community-built guide that catalogues practical tips for improving web page performance.
diff --git a/doc/development/fe_guide/source_editor.md b/doc/development/fe_guide/source_editor.md
new file mode 100644
index 00000000000..fc128c0ecb1
--- /dev/null
+++ b/doc/development/fe_guide/source_editor.md
@@ -0,0 +1,264 @@
+---
+stage: Create
+group: Editor
+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
+---
+
+# Source Editor **(FREE)**
+
+**Source Editor** provides the editing experience at GitLab. This thin wrapper around
+[the Monaco editor](https://microsoft.github.io/monaco-editor/) provides necessary
+helpers and abstractions, and extends Monaco [using extensions](#extensions). Multiple
+GitLab features use it, including:
+
+- [Web IDE](../../user/project/web_ide/index.md)
+- [CI Linter](../../ci/lint.md)
+- [Snippets](../../user/snippets.md)
+- [Web Editor](../../user/project/repository/web_editor.md)
+- [Security Policies](../../user/application_security/threat_monitoring/index.md)
+
+## How to use Source Editor
+
+Source Editor is framework-agnostic and can be used in any application, including both
+Rails and Vue. To help with integration, we have the dedicated `<source-editor>`
+Vue component, but the integration of Source Editor is generally straightforward:
+
+1. Import Source Editor:
+
+ ```javascript
+ import SourceEditor from '~/editor/source_editor';
+ ```
+
+1. Initialize global editor for the view:
+
+ ```javascript
+ const editor = new SourceEditor({
+ // Editor Options.
+ // The list of all accepted options can be found at
+ // https://microsoft.github.io/monaco-editor/api/enums/monaco.editor.editoroption.html
+ });
+ ```
+
+1. Create an editor's instance:
+
+ ```javascript
+ editor.createInstance({
+ // Source Editor configuration options.
+ })
+ ```
+
+An instance of Source Editor accepts the following configuration options:
+
+| Option | Required? | Description |
+| -------------- | ------- | ---- |
+| `el` | `true` | `HTML Node`: The element on which to render the editor. |
+| `blobPath` | `false` | `String`: The name of a file to render in the editor, used to identify the correct syntax highlighter to use with that file, or another file type. Can accept wildcards like `*.js` when the actual filename isn't known or doesn't play any role. |
+| `blobContent` | `false` | `String`: The initial content to render in the editor. |
+| `extensions` | `false` | `Array`: Extensions to use in this instance. |
+| `blobGlobalId` | `false` | `String`: An auto-generated property.<br>**Note:** This property may go away in the future. Do not pass `blobGlobalId` unless you know what you're doing.|
+| Editor Options | `false` | `Object(s)`: Any property outside of the list above is treated as an Editor Option for this particular instance. Use this field to override global Editor Options on the instance level. A full [index of Editor Options](https://microsoft.github.io/monaco-editor/api/enums/monaco.editor.editoroption.html) is available. |
+
+## API
+
+The editor uses the same public API as
+[provided by Monaco editor](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html)
+with additional functions on the instance level:
+
+| Function | Arguments | Description
+| --------------------- | ----- | ----- |
+| `updateModelLanguage` | `path`: String | Updates the instance's syntax highlighting to follow the extension of the passed `path`. Available only on the instance level.|
+| `use` | Array of objects | Array of extensions to apply to the instance. Accepts only the array of _objects_. You must fetch the extensions' ES6 modules must be fetched and resolved in your views or components before they are passed to `use`. This property is available on _instance_ (applies extension to this particular instance) and _global editor_ (applies the same extension to all instances) levels. |
+| Monaco Editor options | See [documentation](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html) | Default Monaco editor options |
+
+## Tips
+
+1. Editor's loading state.
+
+ The loading state is built in to Source Editor, 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 should contain the editor. When bootstrapping,
+ Source Editor shows the loader automatically.
+
+ ![Source Editor: loading state](img/editor_lite_loading.png)
+
+1. Update syntax highlighting if the filename changes.
+
+ ```javascript
+ // fileNameEl here is the HTML input element that contains the file name
+ fileNameEl.addEventListener('change', () => {
+ this.editor.updateModelLanguage(fileNameEl.value);
+ });
+ ```
+
+1. Get the editor's content.
+
+ We may set up listeners on the editor for every change, but it rapidly can become
+ an expensive operation. Instead, get the editor's content when it's needed.
+ For example, on a form's submission:
+
+ ```javascript
+ form.addEventListener('submit', () => {
+ my_content_variable = this.editor.getValue();
+ });
+ ```
+
+1. Performance
+
+ Even though Source Editor itself is extremely slim, it still depends on Monaco editor,
+ which adds weight. Every time you add Source Editor to a view, the JavaScript bundle's
+ size significantly increases, affecting your view's loading performance. We recommend
+ you import the editor on demand if either:
+
+ - You're uncertain if the view needs the editor.
+ - The editor is a secondary element of the view.
+
+ Loading Source Editor on demand is handled like loading any other module:
+
+ ```javascript
+ someActionFunction() {
+ import(/* webpackChunkName: 'SourceEditor' */ '~/editor/source_editor').
+ then(({ default: SourceEditor }) => {
+ const editor = new SourceEditor();
+ ...
+ });
+ ...
+ }
+ ```
+
+## Extensions
+
+Source Editor provides a universal, extensible editing tool to the whole product,
+and doesn't depend on any particular group. Even though the Source Editor's core is owned by
+[Create::Editor FE Team](https://about.gitlab.com/handbook/engineering/development/dev/create-editor/),
+any group can own the extensions—the main functional elements. The goal of
+Source Editor extensions is to keep the editor's core slim and stable. Any
+needed features can be added as extensions to this core. Any group can
+build and own new editing features without worrying about changes to Source Editor
+breaking or overriding them.
+
+You can depend on other modules in your extensions. This organization helps keep
+the size of Source Editor's core at bay by importing dependencies only when needed.
+
+Structurally, the complete implementation of Source Editor can be presented as this diagram:
+
+```mermaid
+graph TD;
+ B[Extension 1]---A[Source Editor]
+ C[Extension 2]---A[Source Editor]
+ D[Extension 3]---A[Source Editor]
+ E[...]---A[Source Editor]
+ F[Extension N]---A[Source Editor]
+ A[Source Editor]---Z[Monaco]
+```
+
+An extension is an ES6 module that exports a JavaScript object:
+
+```javascript
+import { Position } from 'monaco-editor';
+
+export default {
+ navigateFileStart() {
+ this.setPosition(new Position(1, 1));
+ },
+};
+
+```
+
+In the extension's functions, `this` refers to the current Source Editor instance.
+Using `this`, you get access to the complete instance's API, such as the
+`setPosition()` method in this particular case.
+
+### Using an existing extension
+
+Adding an extension to Source Editor's instance requires the following steps:
+
+```javascript
+import SourceEditor from '~/editor/source_editor';
+import MyExtension from '~/my_extension';
+
+const editor = new SourceEditor().createInstance({
+ ...
+});
+editor.use(MyExtension);
+```
+
+### Creating an extension
+
+Let's create our first Source Editor extension. Extensions are
+[ES6 modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/) exporting a
+basic `Object`, used to extend Source Editor's features. As a test, let's
+create an extension that extends Source Editor with a new function that, when called,
+outputs the editor's content in `alert`.
+
+`~/my_folder/my_fancy_extension.js:`
+
+```javascript
+export default {
+ throwContentAtMe() {
+ alert(this.getValue());
+ },
+};
+```
+
+In the code example, `this` refers to the instance. By referring to the instance,
+we can access the complete underlying
+[Monaco editor API](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandalonecodeeditor.html),
+which includes functions like `getValue()`.
+
+Now let's use our extension:
+
+`~/my_folder/component_bundle.js`:
+
+```javascript
+import SourceEditor from '~/editor/source_editor';
+import MyFancyExtension from './my_fancy_extension';
+
+const editor = new SourceEditor().createInstance({
+ ...
+});
+editor.use(MyFancyExtension);
+...
+someButton.addEventListener('click', () => {
+ editor.throwContentAtMe();
+});
+```
+
+First of all, we import Source Editor and our new extension. Then we create the
+editor and its instance. By default Source Editor has no `throwContentAtMe` method.
+But the `editor.use(MyFancyExtension)` line brings that method to our instance.
+After that, we can use it any time we need it. In this case, we call it when some
+theoretical button has been clicked.
+
+This script would result in an alert containing the editor's content when `someButton` is clicked.
+
+![Source Editor new extension's result](img/editor_lite_create_ext.png)
+
+### Tips
+
+1. Performance
+
+ Just like Source Editor itself, any extension can be loaded on demand to not harm
+ loading performance of the views:
+
+ ```javascript
+ const EditorPromise = import(
+ /* webpackChunkName: 'SourceEditor' */ '~/editor/source_editor'
+ );
+ const MarkdownExtensionPromise = import('~/editor/source_editor_markdown_ext');
+
+ Promise.all([EditorPromise, MarkdownExtensionPromise])
+ .then(([{ default: SourceEditor }, { default: MarkdownExtension }]) => {
+ const editor = new SourceEditor().createInstance({
+ ...
+ });
+ editor.use(MarkdownExtension);
+ });
+ ```
+
+1. Using multiple extensions
+
+ Just pass the array of extensions to your `use` method:
+
+ ```javascript
+ editor.use([FileTemplateExtension, MyFancyExtension]);
+ ```
diff --git a/doc/development/fe_guide/storybook.md b/doc/development/fe_guide/storybook.md
new file mode 100644
index 00000000000..15225cc1deb
--- /dev/null
+++ b/doc/development/fe_guide/storybook.md
@@ -0,0 +1,51 @@
+---
+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
+---
+
+# Storybook
+
+The Storybook for the `gitlab-org/gitlab` project is available on our [GitLab Pages site](https://gitlab-org.gitlab.io/gitlab/storybook).
+
+## Storybook in local development
+
+Storybook dependencies and configuration are located under the `storybook/` directory.
+
+To build and launch Storybook locally, in the root directory of the `gitlab` project:
+
+1. Install Storybook dependencies:
+
+ ```shell
+ yarn storybook:install
+ ```
+
+1. Build the Storybook site:
+
+ ```shell
+ yarn storybook:start
+ ```
+
+## Adding components to Storybook
+
+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.
+
+ ```txt
+ vue_shared/
+ ├─ components/
+ │ ├─ sidebar
+ │ │ ├─ 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.
diff --git a/doc/development/fe_guide/style/html.md b/doc/development/fe_guide/style/html.md
index 18f72a9655c..72492d56ee4 100644
--- a/doc/development/fe_guide/style/html.md
+++ b/doc/development/fe_guide/style/html.md
@@ -62,7 +62,7 @@ Avoid forcing links to open in a new window as this reduces the control the user
However, it might be a good idea to use a blank target when replacing the current page with
the link makes the user lose content or progress.
-Use `rel="noopener noreferrer"` whenever your links open in a new window, i.e. `target="_blank"`. This prevents a security vulnerability [documented by JitBit](https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/).
+Use `rel="noopener noreferrer"` whenever your links open in a new window, that is, `target="_blank"`. This prevents a security vulnerability [documented by JitBit](https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/).
When using `gl-link`, using `target="_blank"` is sufficient as it automatically adds `rel="noopener noreferrer"` to the link.
diff --git a/doc/development/fe_guide/style/scss.md b/doc/development/fe_guide/style/scss.md
index c0817626360..4a9446f2949 100644
--- a/doc/development/fe_guide/style/scss.md
+++ b/doc/development/fe_guide/style/scss.md
@@ -51,7 +51,7 @@ We recommend a "utility-first" approach.
1. Start with utility classes.
1. If composing utility classes into a component class removes code duplication and encapsulates a clear responsibility, do it.
-This encourages an organic growth of component classes and prevents the creation of one-off non-reusable classes. Also, the kind of classes that emerge from "utility-first" tend to be design-centered (e.g. `.button`, `.alert`, `.card`) rather than domain-centered (e.g. `.security-report-widget`, `.commit-header-icon`).
+This encourages an organic growth of component classes and prevents the creation of one-off non-reusable classes. Also, the kind of classes that emerge from "utility-first" tend to be design-centered (for example, `.button`, `.alert`, `.card`) rather than domain-centered (for example, `.security-report-widget`, `.commit-header-icon`).
Inspiration:
@@ -139,7 +139,7 @@ To check if any warnings are produced by your changes, run `yarn lint:stylelint`
catch any warnings.
If the Rake task is throwing warnings you don't understand, SCSS Lint's
-documentation includes [a full list of their rules](https://stylelint.io/user-guide/rules/list).
+documentation includes [a full list of their rules](https://stylelint.io/user-guide/rules/list/).
### Fixing issues
diff --git a/doc/development/fe_guide/vue3_migration.md b/doc/development/fe_guide/vue3_migration.md
index d06e93da0f3..7da462a5f89 100644
--- a/doc/development/fe_guide/vue3_migration.md
+++ b/doc/development/fe_guide/vue3_migration.md
@@ -8,7 +8,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Preparations for a Vue 3 migration are tracked in epic [&3174](https://gitlab.com/groups/gitlab-org/-/epics/3174)
-In order to prepare for the eventual migration to Vue 3.x, we should be wary about adding the following features to the codebase:
+In order to prepare for the eventual migration to Vue 3.x, we should not use the following deprecated features in the codebase:
+
+NOTE:
+Our linting rules block the use of these deprecated features.
## Vue filters
@@ -132,3 +135,47 @@ shallowMount(MyAwesomeComponent, {
}
})
```
+
+## Props default function `this` access
+
+**Why?**
+
+In Vue 3, props default value factory functions no longer have access to `this`
+(the component instance).
+
+**What to use instead**
+
+Write a computed prop that resolves the desired value from other props. This
+will work in both Vue 2 and 3.
+
+```html
+<script>
+export default {
+ props: {
+ metric: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ actualTitle() {
+ return this.title ?? this.metric;
+ },
+ },
+}
+
+</script>
+
+<template>
+ <div>{{ actualTitle }}</div>
+</template>
+```
+
+[In Vue 3](https://v3.vuejs.org/guide/migration/props-default-this.html#props-default-function-this-access),
+the props default value factory is passed the raw props as an argument, and can
+also access injections.
diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md
index 3d0044928f1..064f01c8195 100644
--- a/doc/development/fe_guide/vuex.md
+++ b/doc/development/fe_guide/vuex.md
@@ -106,7 +106,7 @@ In this file, we write the actions that call mutations for handling a list of us
.then(({ data }) => commit(types.RECEIVE_USERS_SUCCESS, data))
.catch((error) => {
commit(types.RECEIVE_USERS_ERROR, error)
- createFlash('There was an error')
+ createFlash({ message: 'There was an error' })
});
}
@@ -540,11 +540,11 @@ export default {
foo: ''
},
actions: {
- updateBar() {...}
- updateAll() {...}
+ updateBar() {...},
+ updateAll() {...},
},
getters: {
- getFoo() {...}
+ getFoo() {...},
}
}
```
@@ -559,13 +559,13 @@ export default {
* @param {string} list[].getter - the name of the getter, leave it empty to not use a getter
* @param {string} list[].updateFn - the name of the action, leave it empty to use the default action
* @param {string} defaultUpdateFn - the default function to dispatch
- * @param {string} root - optional key of the state where to search fo they keys described in list
+ * @param {string|function} root - optional key of the state where to search for they keys described in list
* @returns {Object} a dictionary with all the computed properties generated
*/
...mapComputed(
[
'baz',
- { key: 'bar', updateFn: 'updateBar' }
+ { key: 'bar', updateFn: 'updateBar' },
{ key: 'foo', getter: 'getFoo' },
],
'updateAll',
@@ -575,3 +575,48 @@ export default {
```
`mapComputed` then generates the appropriate computed properties that get the data from the store and dispatch the correct action when updated.
+
+In the event that the `root` of the key is more than one-level deep you can use a function to retrieve the relevant state object.
+
+For instance, with a store like:
+
+```javascript
+// this store is non-functional and only used to give context to the example
+export default {
+ state: {
+ foo: {
+ qux: {
+ baz: '',
+ bar: '',
+ foo: '',
+ },
+ },
+ },
+ actions: {
+ updateBar() {...},
+ updateAll() {...},
+ },
+ getters: {
+ getFoo() {...},
+ }
+}
+```
+
+The `root` could be:
+
+```javascript
+import { mapComputed } from '~/vuex_shared/bindings'
+export default {
+ computed: {
+ ...mapComputed(
+ [
+ 'baz',
+ { key: 'bar', updateFn: 'updateBar' },
+ { key: 'foo', getter: 'getFoo' },
+ ],
+ 'updateAll',
+ (state) => state.foo.qux,
+ ),
+ }
+}
+```