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:
Diffstat (limited to 'doc/development/new_fe_guide/modules/widget_extensions.md')
-rw-r--r--doc/development/new_fe_guide/modules/widget_extensions.md358
1 files changed, 7 insertions, 351 deletions
diff --git a/doc/development/new_fe_guide/modules/widget_extensions.md b/doc/development/new_fe_guide/modules/widget_extensions.md
index 4bae0ac70c4..3741ee8c38a 100644
--- a/doc/development/new_fe_guide/modules/widget_extensions.md
+++ b/doc/development/new_fe_guide/modules/widget_extensions.md
@@ -1,355 +1,11 @@
---
-stage: Create
-group: Code Review
-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: '../../fe_guide/merge_request_widget_extensions.md'
+remove_date: '2022-11-15'
---
-# Merge request widget extensions **(FREE)**
+This document was moved to [another location](../../fe_guide/merge_request_widget_extensions.md).
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44616) in GitLab 13.6.
-
-## Summary
-
-Extensions in the merge request widget enable you to add new features
-into the merge request widget that match the design framework.
-With extensions we get a lot of benefits out of the box without much effort required, like:
-
-- A consistent look and feel.
-- Tracking when the extension is opened.
-- Virtual scrolling for performance.
-
-## Usage
-
-To use extensions you must first create a new extension object to fetch the
-data to render in the extension. For a working example, refer to the example file in
-`app/assets/javascripts/vue_merge_request_widget/extensions/issues.js`.
-
-The basic object structure:
-
-```javascript
-export default {
- name: '', // Required: This helps identify the widget
- props: [], // Required: Props passed from the widget state
- i18n: { // Required: Object to hold i18n text
- label: '', // Required: Used for tooltips and aria-labels
- loading: '', // Required: Loading text for when data is loading
- },
- expandEvent: '', // Optional: RedisHLL event name to track expanding content
- enablePolling: false, // Optional: Tells extension to poll for data
- modalComponent: null, // Optional: The component to use for the modal
- computed: {
- summary(data) {}, // Required: Level 1 summary text
- statusIcon(data) {}, // Required: Level 1 status icon
- tertiaryButtons() {}, // Optional: Level 1 action buttons
- shouldCollapse() {}, // Optional: Add logic to determine if the widget can expand or not
- },
- methods: {
- fetchCollapsedData(props) {}, // Required: Fetches data required for collapsed state
- fetchFullData(props) {}, // Required: Fetches data for the full expanded content
- fetchMultiData() {}, // Optional: Works in conjunction with `enablePolling` and allows polling multiple endpoints
- },
-};
-```
-
-By following the same data structure, each extension can follow the same registering structure,
-but each extension can manage its data sources.
-
-After creating this structure, you must register it. You can register the extension at any
-point _after_ the widget has been created. To register a extension:
-
-```javascript
-// Import the register method
-import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
-
-// Import the new extension
-import issueExtension from '~/vue_merge_request_widget/extensions/issues';
-
-// Register the imported extension
-registerExtension(issueExtension);
-```
-
-## Data fetching
-
-Each extension must fetch data. Fetching is handled when registering the extension,
-not by the core component itself. This approach allows for various different
-data fetching methods to be used, such as GraphQL or REST API calls.
-
-### API calls
-
-For performance reasons, it is best if the collapsed state fetches only the data required to
-render the collapsed state. This fetching happens within the `fetchCollapsedData` method.
-This method is called with the props as an argument, so you can easily access
-any paths set in the state.
-
-To allow the extension to set the data, this method **must** return the data. No
-special formatting is required. When the extension receives this data,
-it is set to `collapsedData`. You can access `collapsedData` in any computed property or
-method.
-
-When the user clicks **Expand**, the `fetchFullData` method is called. This method
-also gets called with the props as an argument. This method **must** also return
-the full data. However, this data needs to be correctly formatted to match the format
-mentioned in the data structure section.
-
-#### Technical debt
-
-For some of the current extensions, there is no split in data fetching. All the data
-is fetched through the `fetchCollapsedData` method. While less performant,
-it allows for faster iteration.
-
-To handle this the `fetchFullData` returns the data set through
-the `fetchCollapsedData` method call. In these cases, the `fetchFullData` must
-return a promise:
-
-```javascript
-fetchCollapsedData() {
- return ['Some data'];
-},
-fetchFullData() {
- return Promise.resolve(this.collapsedData)
-},
-```
-
-### Data structure
-
-The data returned from `fetchFullData` must match the format below. This format
-allows the core component to render the data in a way that matches
-the design framework. Any text properties can use the styling placeholders
-mentioned below:
-
-```javascript
-{
- id: data.id, // Required: ID used as a key for each row
- header: 'Header' || ['Header', 'sub-header'], // Required: String or array can be used for the header text
- text: '', // Required: Main text for the row
- subtext: '', // Optional: Smaller sub-text to be displayed below the main text
- icon: { // Optional: Icon object
- name: EXTENSION_ICONS.success, // Required: The icon name for the row
- },
- badge: { // Optional: Badge displayed after text
- text: '', // Required: Text to be displayed inside badge
- variant: '', // Optional: GitLab UI badge variant, defaults to info
- },
- link: { // Optional: Link to a URL displayed after text
- text: '', // Required: Text of the link
- href: '', // Optional: URL for the link
- },
- modal: { // Optional: Link to open a modal displayed after text
- text: '', // Required: Text of the link
- onClick: () => {} // Optional: Function to run when link is clicked, i.e. to set this.modalData
- }
- actions: [], // Optional: Action button for row
- children: [], // Optional: Child content to render, structure matches the same structure
-}
-```
-
-### Polling
-
-To enable polling for an extension, an options flag must be present in the extension:
-
-```javascript
-export default {
- //...
- enablePolling: true
-};
-```
-
-This flag tells the base component we should poll the `fetchCollapsedData()`
-defined in the extension. Polling stops if the response has data, or if an error is present.
-
-When writing the logic for `fetchCollapsedData()`, a complete Axios response must be returned
-from the method. The polling utility needs data like polling headers to work correctly:
-
-```javascript
-export default {
- //...
- enablePolling: true
- methods: {
- fetchCollapsedData() {
- return axios.get(this.reportPath)
- },
- },
-};
-```
-
-Most of the time the data returned from the extension's endpoint is not in the format
-the UI needs. We must format the data before setting the collapsed data in the base component.
-
-If the computed property `summary` can rely on `collapsedData`, you can format the data
-when `fetchFullData` is invoked:
-
-```javascript
-export default {
- //...
- enablePolling: true
- methods: {
- fetchCollapsedData() {
- return axios.get(this.reportPath)
- },
- fetchFullData() {
- return Promise.resolve(this.prepareReports());
- },
- // custom method
- prepareReports() {
- // unpack values from collapsedData
- const { new_errors, existing_errors, resolved_errors } = this.collapsedData;
-
- // perform data formatting
-
- return [...newErrors, ...existingErrors, ...resolvedErrors]
- }
- },
-};
-```
-
-If the extension relies on `collapsedData` being formatted before invoking `fetchFullData()`,
-then `fetchCollapsedData()` must return the Axios response as well as the formatted data:
-
-```javascript
-export default {
- //...
- enablePolling: true
- methods: {
- fetchCollapsedData() {
- return axios.get(this.reportPath).then(res => {
- const formattedData = this.prepareReports(res.data)
-
- return {
- ...res,
- data: formattedData,
- }
- })
- },
- // Custom method
- prepareReports() {
- // Unpack values from collapsedData
- const { new_errors, existing_errors, resolved_errors } = this.collapsedData;
-
- // Perform data formatting
-
- return [...newErrors, ...existingErrors, ...resolvedErrors]
- }
- },
-};
-```
-
-If the extension needs to poll multiple endpoints at the same time, then `fetchMultiData`
-can be used to return an array of functions. A new `poll` object is created for each
-endpoint and they are polled separately. After all endpoints are resolved, polling is
-stopped and `setCollapsedData` is called with an array of `response.data`.
-
-```javascript
-export default {
- //...
- enablePolling: true
- methods: {
- fetchMultiData() {
- return [
- () => axios.get(this.reportPath1),
- () => axios.get(this.reportPath2),
- () => axios.get(this.reportPath3)
- },
- },
-};
-```
-
-**Important** The function needs to return a `Promise` that resolves the `response` object.
-The implementation relies on the `POLL-INTERVAL` header to keep polling, therefore it is
-important not to alter the status code and headers.
-
-### Errors
-
-If `fetchCollapsedData()` or `fetchFullData()` methods throw an error:
-
-- The loading state of the extension is updated to `LOADING_STATES.collapsedError`
- and `LOADING_STATES.expandedError` respectively.
-- The extensions header displays an error icon and updates the text to be either:
- - The text defined in `$options.i18n.error`.
- - "Failed to load" if `$options.i18n.error` is not defined.
-- The error is sent to Sentry to log that it occurred.
-
-To customise the error text, add it to the `i18n` object in your extension:
-
-```javascript
-export default {
- //...
- i18n: {
- //...
- error: __('Your error text'),
- },
-};
-```
-
-## Icons
-
-Level 1 and all subsequent levels can have their own status icons. To keep with
-the design framework, import the `EXTENSION_ICONS` constant
-from the `constants.js` file:
-
-```javascript
-import { EXTENSION_ICONS } from '~/vue_merge_request_widget/constants.js';
-```
-
-This constant has the below icons available for use. Per the design framework,
-only some of these icons should be used on level 1:
-
-- `failed`
-- `warning`
-- `success`
-- `neutral`
-- `error`
-- `notice`
-- `severityCritical`
-- `severityHigh`
-- `severityMedium`
-- `severityLow`
-- `severityInfo`
-- `severityUnknown`
-
-## Text styling
-
-Any area that has text can be styled with the placeholders below. This
-technique follows the same technique as `sprintf`. However, instead of specifying
-these through `sprintf`, the extension does this automatically.
-
-Every placeholder contains starting and ending tags. For example, `success` uses
-`Hello %{success_start}world%{success_end}`. The extension then
-adds the start and end tags with the correct styling classes.
-
-| Placeholder | Style |
-|---|---|
-| success | `gl-font-weight-bold gl-text-green-500` |
-| danger | `gl-font-weight-bold gl-text-red-500` |
-| critical | `gl-font-weight-bold gl-text-red-800` |
-| same | `gl-font-weight-bold gl-text-gray-700` |
-| strong | `gl-font-weight-bold` |
-| small | `gl-font-sm` |
-
-## Action buttons
-
-You can add action buttons to all level 1 and 2 in each extension. These buttons
-are meant as a way to provide links or actions for each row:
-
-- Action buttons for level 1 can be set through the `tertiaryButtons` computed property.
- This property should return an array of objects for each action button.
-- Action buttons for level 2 can be set by adding the `actions` key to the level 2 rows object.
- The value for this key must also be an array of objects for each action button.
-
-Links must follow this structure:
-
-```javascript
-{
- text: 'Click me',
- href: this.someLinkHref,
- target: '_blank', // Optional
-}
-```
-
-For internal action buttons, follow this structure:
-
-```javascript
-{
- text: 'Click me',
- onClick() {}
-}
-```
+<!-- This redirect file can be deleted after <2022-11-15>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->