Merge request widgets enable you to add new features that match the design framework. With these widgets we get a lot of benefits out of the box without much effort required, like:
The widgets are regular Vue components that make use of the ~/vue_merge_request_widget/components/widget/widget.vue
component. Depending on the complexity of the use case, it is possible to pass down configuration objects, or extend the component through slot
s.
For an example that uses slots, refer to the following file: ee/app/assets/javascripts/vue_merge_request_widget/widgets/security_reports/mr_widget_security_reports.vue
For an example that uses data objects, refer to the following file: ee/app/assets/javascripts/vue_merge_request_widget/widgets/metrics/index.vue
Here is a minimal example that renders a Hello World widget:
<script>
import MrWidget from '~/vue_merge_request_widget/components/widget/widget.vue';
import { __ } from '~/locale';
export default {
name: 'WidgetHelloWorld',
components: {
MrWidget,
},
computed: {
summary() {
return { title: __('Hello World') };
},
},
};
</script>
<template>
<mr-widget :summary="summary" :is-collapsible="false" :widget-name="$options.name" />
</template>
Registering widgets
The example above won’t be rendered anywhere in the page. In order to mount it in the Merge Request Widget section, we have to register the widget in one or both of these two locations:
app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
(for CE widgets)ee/app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue
(for CE and EE widgets)Defining the component in the components list and adding the name to the widgets
computed property will mount the widget:
<script>
export default {
components: {
MrHelloWorldWidget: () =>
import('ee/vue_merge_request_widget/widgets/hello_world/index.vue'),
},
computed: {
mrHelloWorldWidget() {
return this.mr.shouldRenderHelloWorldWidget ? 'MrHelloWorldWidget' : undefined;
},
widgets() {
return [
this.mrHelloWorldWidget,
].filter((w) => w);
},
},
};
</script>
Data fetching
To fetch data when the widget is mounted, pass the :fetch-collapsed-data
property a function that performs an API call.
The function must return a Promise
that resolves to 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.
<script>
export default {
// ...
data() {
return {
collapsedData: [],
};
},
methods: {
fetchCollapsedData() {
return axios.get('/my/path').then((response) => {
this.collapsedData = response.data;
return response;
});
},
},
};
</script>
<template>
<mr-widget :fetch-collapsed-data="fetchCollapsedData" />
</template>
:fetch-expanded-data
works the same way, but it will be called only when the user expands the widget.
The content
and summary
properties can be used to render the Widget
. Below is the documentation for both properties:
// content
{
text: '', // Required: Main text for the row
subtext: '', // Optional: Smaller sub-text to be displayed below the main text
supportingText: '', // Optional: Paragraph to be displayed below the subtext
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
},
actions: [], // Optional: Action button for row
children: [], // Optional: Child content to render, structure matches the same structure
helpPopover: { // Optional: If provided, an information icon will be display at the right-most corner of the content row
options: {
title: '' // Required: The title of the popover
},
content: {
text: '', // Optional: Text content of the popover
learnMorePath: '', // Optional: The path to the documentation. A learn more link will be displayed if provided.
}
}
}
// summary
{
title: '', // Required: The main text of the summary part
subtitle: '', // Optional: The subtext of the summary part
}
Errors
If :fetch-collapsed-data
or :fetch-expanded-data
methods throw an error. To customise the error text, you can use the :error-text
property:
<template>
<mr-widget :error-text="__('Failed to load.')" />
</template>
Telemetry
The base implementation of the widget framework includes some telemetry events. Each widget reports:
view
: When it is rendered to the screen.expand
: When it is expanded.full_report_clicked
: When an (optional) input is clicked to view the full report.expand_success
, expand_warning
, or expand_failed
): One of three additional events relating to the status of the widget when it was expanded.When adding new widgets, the above events must be marked as known
, and have metrics created, to be reportable.
Events that are only for EE should include --ee
at the end of both shell commands below.
To generate these known events for a single widget:
Widgets should be named Widget${CamelName}
.
WidgetTestReports
.Compute the widget name slug by converting the ${CamelName}
to lower-, snake-case.
test_reports
.Add the new widget name slug to lib/gitlab/usage_data_counters/merge_request_widget_counter.rb
in the WIDGETS
list.
Ensure the GDK is running (gdk start
).
Generate known events on the command line with the following command. Replace test_reports
with your appropriate name slug:
bundle exec rails generate gitlab:usage_metric_definition \
counts.i_code_review_merge_request_widget_test_reports_count_view \
counts.i_code_review_merge_request_widget_test_reports_count_full_report_clicked \
counts.i_code_review_merge_request_widget_test_reports_count_expand \
counts.i_code_review_merge_request_widget_test_reports_count_expand_success \
counts.i_code_review_merge_request_widget_test_reports_count_expand_warning \
counts.i_code_review_merge_request_widget_test_reports_count_expand_failed \
--dir=all
Modify each newly generated file to match the existing files for the merge request widget extension telemetry.
metrics/**/*_i_code_review_merge_request_widget_*
description
= A plain English description of this value. Review existing widget extension telemetry files for examples.product_section
= dev
product_stage
= create
product_group
= code_review
introduced_by_url
= '[your MR]'
options.events
= (the event in the command from above that generated this file, like i_code_review_merge_request_widget_test_reports_count_view
)
data_source
= redis
data_category
= optional
Generate known HLL events on the command line with the following command. Replace test_reports
with your appropriate name slug.
bundle exec rails generate gitlab:usage_metric_definition:redis_hll code_review \
i_code_review_merge_request_widget_test_reports_view \
i_code_review_merge_request_widget_test_reports_full_report_clicked \
i_code_review_merge_request_widget_test_reports_expand \
i_code_review_merge_request_widget_test_reports_expand_success \
i_code_review_merge_request_widget_test_reports_expand_warning \
i_code_review_merge_request_widget_test_reports_expand_failed \
--class_name=RedisHLLMetric
Repeat step 6, but change the data_source
to redis_hll
.
Add each event (those listed in the command in step 7, replacing test_reports
with the appropriate name slug) to the aggregate files:
config/metrics/counts_7d/{timestamp}_code_review_category_monthly_active_users.yml
config/metrics/counts_7d/{timestamp}_code_review_group_monthly_active_users.yml
config/metrics/counts_28d/{timestamp}_code_review_category_monthly_active_users.yml
config/metrics/counts_28d/{timestamp}_code_review_group_monthly_active_users.yml
If you are adding a new event to our known events, include the new event in the KNOWN_EVENTS
list in lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb
.
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:
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
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:
tertiaryButtons
computed property. This property should return an array of objects for each action button.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:
{
text: 'Click me',
href: this.someLinkHref,
target: '_blank', // Optional
}
For internal action buttons, follow this structure:
{
text: 'Click me',
onClick() {}
}
Demo
Visit GitLab MR Widgets Demo to see an example of all widgets displayed together.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4