diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 14:10:13 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 14:10:13 +0300 |
commit | 0ea3fcec397b69815975647f5e2aa5fe944a8486 (patch) | |
tree | 7979381b89d26011bcf9bdc989a40fcc2f1ed4ff /app/assets/javascripts/vue_merge_request_widget/components | |
parent | 72123183a20411a36d607d70b12d57c484394c8e (diff) |
Add latest changes from gitlab-org/gitlab@15-1-stable-eev15.1.0-rc42
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget/components')
19 files changed, 418 insertions, 184 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue index e7d5e4086bc..4163d195e0f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue @@ -1,5 +1,5 @@ <script> -import { GlButton } from '@gitlab/ui'; +import { GlButton, GlSprintf, GlLink } from '@gitlab/ui'; import createFlash from '~/flash'; import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; @@ -11,9 +11,11 @@ import eventHub from '../../event_hub'; import approvalsMixin from '../../mixins/approvals'; import MrWidgetContainer from '../mr_widget_container.vue'; import MrWidgetIcon from '../mr_widget_icon.vue'; +import { INVALID_RULES_DOCS_PATH } from '../../constants'; import ApprovalsSummary from './approvals_summary.vue'; import ApprovalsSummaryOptional from './approvals_summary_optional.vue'; import { FETCH_LOADING, FETCH_ERROR, APPROVE_ERROR, UNAPPROVE_ERROR } from './messages'; +import { humanizeInvalidApproversRules } from './humanized_text'; export default { name: 'MRWidgetApprovals', @@ -23,6 +25,8 @@ export default { ApprovalsSummary, ApprovalsSummaryOptional, GlButton, + GlSprintf, + GlLink, }, mixins: [approvalsMixin, glFeatureFlagsMixin()], props: { @@ -78,6 +82,15 @@ export default { approvals() { return this.mr.approvals || {}; }, + invalidRules() { + return this.approvals.invalid_approvers_rules || []; + }, + hasInvalidRules() { + return this.approvals.merge_request_approvers_available && this.invalidRules.length; + }, + invalidRulesText() { + return humanizeInvalidApproversRules(this.invalidRules); + }, approvedBy() { return this.approvals.approved_by ? this.approvals.approved_by.map((x) => x.user) : []; }, @@ -104,20 +117,24 @@ export default { return { text: this.approvalText, category: this.isApproved ? 'secondary' : 'primary', - variant: 'info', + variant: 'confirm', action: () => this.approve(), }; } else if (this.showUnapprove) { return { text: s__('mrWidget|Revoke approval'), - variant: 'warning', - category: 'secondary', + variant: 'default', action: () => this.unapprove(), }; } return null; }, + pluralizedRuleText() { + return this.invalidRules.length > 1 + ? this.$options.i18n.invalidRulesPlural + : this.$options.i18n.invalidRuleSingular; + }, }, created() { this.refreshApprovals() @@ -194,6 +211,16 @@ export default { }, }, FETCH_LOADING, + linkToInvalidRules: INVALID_RULES_DOCS_PATH, + i18n: { + invalidRuleSingular: s__( + 'mrWidget|Approval rule %{rules} is invalid. GitLab has approved this rule automatically to unblock the merge request. %{link}', + ), + invalidRulesPlural: s__( + 'mrWidget|Approval rules %{rules} are invalid. GitLab has approved these rules automatically to unblock the merge request. %{link}', + ), + learnMore: __('Learn more.'), + }, }; </script> <template> @@ -202,29 +229,45 @@ export default { <mr-widget-icon name="approval" /> <div v-if="fetchingApprovals">{{ $options.FETCH_LOADING }}</div> <template v-else> - <gl-button - v-if="action" - :variant="action.variant" - :category="action.category" - :loading="isApproving" - class="mr-3" - data-qa-selector="approve_button" - @click="action.action" - > - {{ action.text }} - </gl-button> - <approvals-summary-optional - v-if="isOptional" - :can-approve="hasAction" - :help-path="mr.approvalsHelpPath" - /> - <approvals-summary - v-else - :approved="isApproved" - :approvals-left="approvals.approvals_left || 0" - :rules-left="approvals.approvalRuleNamesLeft" - :approvers="approvedBy" - /> + <div class="gl-display-flex gl-flex-direction-column"> + <div class="gl-display-flex gl-flex-direction-row gl-align-items-center"> + <gl-button + v-if="action" + :variant="action.variant" + :category="action.category" + :loading="isApproving" + class="gl-mr-5" + data-qa-selector="approve_button" + @click="action.action" + > + {{ action.text }} + </gl-button> + <approvals-summary-optional + v-if="isOptional" + :can-approve="hasAction" + :help-path="mr.approvalsHelpPath" + /> + <approvals-summary + v-else + :approved="isApproved" + :approvals-left="approvals.approvals_left || 0" + :rules-left="approvals.approvalRuleNamesLeft" + :approvers="approvedBy" + /> + </div> + <div v-if="hasInvalidRules" class="gl-text-gray-400 gl-mt-2" data-testid="invalid-rules"> + <gl-sprintf :message="pluralizedRuleText"> + <template #rules> + {{ invalidRulesText }} + </template> + <template #link> + <gl-link :href="$options.linkToInvalidRules" target="_blank"> + {{ $options.i18n.learnMore }} + </gl-link> + </template> + </gl-sprintf> + </div> + </div> <slot :is-approving="isApproving" :approve-with-auth="approveWithAuth" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue index 0e31f97b9db..b1c4f7c5a7c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue @@ -98,10 +98,10 @@ export default { <template> <div data-qa-selector="approvals_summary_content"> - <strong>{{ approvalLeftMessage }}</strong> + <span class="gl-font-weight-bold">{{ approvalLeftMessage }}</span> <template v-if="hasApprovers"> <span v-if="approvalLeftMessage">{{ message }}</span> - <strong v-else>{{ message }}</strong> + <span v-else class="gl-font-weight-bold">{{ message }}</span> <user-avatar-list class="gl-display-inline-block gl-vertical-align-middle" :img-size="24" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/humanized_text.js b/app/assets/javascripts/vue_merge_request_widget/components/approvals/humanized_text.js new file mode 100644 index 00000000000..6689d070053 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/humanized_text.js @@ -0,0 +1,23 @@ +import { __ } from '~/locale'; + +const humanizeRules = (invalidRules) => { + if (invalidRules.length > 1) { + return invalidRules.reduce((rules, { name }, index) => { + if (index === invalidRules.length - 1) { + return `${rules}${__(' and ')}"${name}"`; + } + return rules ? `${rules}, "${name}"` : `"${name}"`; + }, ''); + } + return `"${invalidRules[0].name}"`; +}; + +export const humanizeInvalidApproversRules = (invalidRules) => { + const ruleCount = invalidRules.length; + + if (!ruleCount) { + return ''; + } + + return humanizeRules(invalidRules); +}; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/actions.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/actions.vue index d878a1fa2e0..655ceb5f700 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/actions.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/actions.vue @@ -26,6 +26,7 @@ export default { }, methods: { onClickAction(action) { + this.$emit('clickedAction', action); if (action.onClick) { action.onClick(); } diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue index 0bc17de638b..4ba620da00a 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue @@ -6,16 +6,16 @@ import { GlTooltipDirective, GlIntersectionObserver, } from '@gitlab/ui'; -import { once } from 'lodash'; import * as Sentry from '@sentry/browser'; import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller'; -import api from '~/api'; import { sprintf, s__, __ } from '~/locale'; import Poll from '~/lib/utils/poll'; +import { normalizeHeaders } from '~/lib/utils/common_utils'; import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants'; import StatusIcon from './status_icon.vue'; import Actions from './actions.vue'; import ChildContent from './child_content.vue'; +import { createTelemetryHub } from './telemetry'; import { generateText } from './utils'; export const LOADING_STATES = { @@ -26,6 +26,7 @@ export const LOADING_STATES = { }; export default { + telemetry: true, components: { GlButton, GlLoadingIcon, @@ -49,6 +50,7 @@ export default { showFade: false, modalData: undefined, modalName: undefined, + telemetry: null, }; }, computed: { @@ -131,50 +133,85 @@ export default { } }, }, + created() { + if (this.$options.telemetry) { + this.telemetry = createTelemetryHub(this.$options.name); + } + }, mounted() { this.loadCollapsedData(); + + this.telemetry?.viewed(); }, methods: { - triggerRedisTracking: once(function triggerRedisTracking() { - if (this.$options.expandEvent) { - api.trackRedisHllUserEvent(this.$options.expandEvent); - } - }), toggleCollapsed(e) { if (this.isCollapsible && !e?.target?.closest('.btn:not(.btn-icon),a')) { - this.isCollapsed = !this.isCollapsed; + if (this.isCollapsed) { + this.telemetry?.expanded({ type: this.statusIconName }); + } - this.triggerRedisTracking(); + this.isCollapsed = !this.isCollapsed; } }, + initExtensionMultiPolling() { + const allData = []; + const requests = this.fetchMultiData(); + + requests.forEach((request) => { + const poll = new Poll({ + resource: { + fetchData: () => request(this), + }, + method: 'fetchData', + successCallback: (response) => { + this.headerCheck(response, (data) => allData.push(data)); + + if (allData.length === requests.length) { + this.setCollapsedData(allData); + } + }, + errorCallback: (e) => { + this.setCollapsedError(e); + }, + }); + + poll.makeRequest(); + }); + }, initExtensionPolling() { const poll = new Poll({ resource: { - fetchData: () => this.fetchCollapsedData(this.$props), + fetchData: () => this.fetchCollapsedData(this), }, method: 'fetchData', - successCallback: ({ data }) => { - if (Object.keys(data).length > 0) { - poll.stop(); - this.setCollapsedData(data); - } + successCallback: (response) => { + this.headerCheck(response, (data) => this.setCollapsedData(data)); }, errorCallback: (e) => { - poll.stop(); - this.setCollapsedError(e); }, }); poll.makeRequest(); }, + headerCheck(response, callback) { + const headers = normalizeHeaders(response.headers); + + if (!headers['POLL-INTERVAL']) { + callback(response.data); + } + }, loadCollapsedData() { this.loadingState = LOADING_STATES.collapsedLoading; if (this.$options.enablePolling) { - this.initExtensionPolling(); + if (this.fetchMultiData) { + this.initExtensionMultiPolling(); + } else { + this.initExtensionPolling(); + } } else { - this.fetchCollapsedData(this.$props) + this.fetchCollapsedData(this) .then((data) => { this.setCollapsedData(data); }) @@ -197,7 +234,7 @@ export default { this.loadingState = LOADING_STATES.expandedLoading; - this.fetchFullData(this.$props) + this.fetchFullData(this) .then((data) => { this.loadingState = null; this.fullData = data.map((x, i) => ({ id: i, ...x })); @@ -231,6 +268,11 @@ export default { this.toggleCollapsed(e); } }, + onClickedAction(action) { + if (action.fullReport) { + this.telemetry?.fullReportClicked(); + } + }, generateText, }, EXTENSION_ICON_CLASS, @@ -268,6 +310,7 @@ export default { <actions :widget="$options.label || $options.name" :tertiary-buttons="tertiaryActionsButtons" + @clickedAction="onClickedAction" /> <div v-if="isCollapsible" @@ -324,6 +367,7 @@ export default { :widget-label="widgetLabel" :modal-id="modalId" :level="2" + @clickedAction="onClickedAction" /> </gl-intersection-observer> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue index 0ca4c92a5ae..38f83a61b30 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue @@ -39,6 +39,9 @@ export default { isArray(arr) { return Array.isArray(arr); }, + onClickedAction(action) { + this.$emit('clickedAction', action); + }, generateText, }, }; @@ -63,14 +66,14 @@ export default { <div class="gl-w-full"> <div class="gl-display-flex gl-flex-nowrap"> <div class="gl-flex-wrap gl-display-flex gl-w-full"> - <div class="gl-mr-4 gl-display-flex gl-align-items-center"> + <div class="gl-display-flex gl-align-items-center"> <p v-safe-html="generateText(data.text)" class="gl-m-0"></p> </div> - <div v-if="data.link"> + <div v-if="data.link" class="gl-pr-2"> <gl-link :href="data.link.href">{{ data.link.text }}</gl-link> </div> - <div v-if="data.modal"> - <gl-link v-gl-modal="modalId" @click="data.modal.onClick"> + <div v-if="data.modal" class="gl-pr-2"> + <gl-link v-gl-modal="modalId" data-testid="modal-link" @click="data.modal.onClick"> {{ data.modal.text }} </gl-link> </div> @@ -81,7 +84,12 @@ export default { {{ data.badge.text }} </gl-badge> </div> - <actions :widget="widgetLabel" :tertiary-buttons="data.actions" class="gl-ml-auto" /> + <actions + :widget="widgetLabel" + :tertiary-buttons="data.actions" + class="gl-ml-auto gl-pl-3" + @clickedAction="onClickedAction" + /> </div> <p v-if="data.subtext" @@ -101,6 +109,7 @@ export default { :modal-id="modalId" :level="3" data-testid="child-content" + @clickedAction="onClickedAction" /> </li> </ul> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js index b9dfd3bd41e..a58d524b9ed 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js @@ -14,7 +14,7 @@ export default { if (extensions.length === 0) return null; return h( - 'div', + 'section', { attrs: { role: 'region', @@ -34,13 +34,7 @@ export default { { ...extension }, { props: { - ...extension.props.reduce( - (acc, key) => ({ - ...acc, - [key]: this.mr[key], - }), - {}, - ), + mr: this.mr, }, }, ), diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js index 65273678fb9..f4fcf4c9571 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/index.js @@ -10,12 +10,27 @@ export const registerExtension = (extension) => { registeredExtensions.extensions.push({ extends: ExtensionBase, name: extension.name, - props: extension.props, + props: { + mr: { + type: Object, + required: true, + }, + }, + telemetry: extension.telemetry, i18n: extension.i18n, expandEvent: extension.expandEvent, enablePolling: extension.enablePolling, modalComponent: extension.modalComponent, computed: { + ...extension.props.reduce( + (acc, propKey) => ({ + ...acc, + [propKey]() { + return this.mr[propKey]; + }, + }), + {}, + ), ...Object.keys(extension.computed).reduce( (acc, computedKey) => ({ ...acc, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue index 456a1f17aae..bb626c9adba 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue @@ -49,7 +49,7 @@ export default { ]" class="gl-rounded-full gl-mr-3 gl-relative gl-p-2" > - <gl-loading-icon v-if="isLoading" size="lg" inline class="gl-display-block" /> + <gl-loading-icon v-if="isLoading" size="sm" inline class="gl-display-block" /> <gl-icon v-else :name="$options.EXTENSION_ICON_NAMES[iconName]" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js new file mode 100644 index 00000000000..aec3a35f37c --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js @@ -0,0 +1,207 @@ +import api from '~/api'; +import createEventHub from '~/helpers/event_hub_factory'; +import { + TELEMETRY_WIDGET_VIEWED, + TELEMETRY_WIDGET_EXPANDED, + TELEMETRY_WIDGET_FULL_REPORT_CLICKED, +} from '../../constants'; + +/* + * Additional events to send beyond the defaults for certain widget extensions + */ +const nonStandardEvents = { + codeQuality: { + uniqueUser: { + expand: ['i_testing_code_quality_widget_total'], + }, + counter: {}, + }, + terraform: { + uniqueUser: { + expand: ['i_testing_terraform_widget_total'], + }, + counter: {}, + }, + issues: { + uniqueUser: { + expand: ['i_testing_load_performance_widget_total'], + }, + counter: {}, + }, + testReport: { + uniqueUser: { + expand: ['i_testing_summary_widget_total'], + }, + counter: {}, + }, +}; + +function combineDeepArray(path, ...objects) { + const parts = path.split('.'); + const allEntries = objects.reduce((entries, currentObject) => { + let expandedEntries = entries; + let traversed = currentObject; + + parts.forEach((part) => { + traversed = traversed?.[part]; + }); + + if (traversed) { + expandedEntries = [...entries, ...traversed]; + } + + return expandedEntries; + }, []); + + return Array.from(new Set(allEntries)); +} + +function simplifyWidgetName(componentName) { + const noWidget = componentName.replace(/^Widget/, ''); + + return noWidget.charAt(0).toLowerCase() + noWidget.slice(1); +} + +function baseRedisEventName(extensionName) { + const redisEventName = extensionName.replace(/([A-Z])/g, '_$1').toLowerCase(); + + return `i_merge_request_widget_${redisEventName}`; +} + +function whenable(bus) { + return function when(event) { + return ({ execute, track, special }) => { + bus.$on(event, (busEvent) => { + track.forEach((redisEvent) => { + execute(redisEvent); + }); + + special?.({ event, execute, track, bus, busEvent }); + }); + }; + }; +} + +function defaultBehaviorEvents({ bus, config }) { + const when = whenable(bus); + const isViewed = when(TELEMETRY_WIDGET_VIEWED); + const isExpanded = when(TELEMETRY_WIDGET_EXPANDED); + const fullReportIsClicked = when(TELEMETRY_WIDGET_FULL_REPORT_CLICKED); + const toHll = config?.uniqueUser || {}; + const toCounts = config?.counter || {}; + const user = api.trackRedisHllUserEvent.bind(api); + const count = api.trackRedisCounterEvent.bind(api); + + if (toHll.view) { + isViewed({ execute: user, track: toHll.view }); + } + if (toCounts.view) { + isViewed({ execute: count, track: toCounts.view }); + } + + if (toHll.expand) { + isExpanded({ + execute: user, + track: toHll.expand, + special: ({ execute, track, busEvent }) => { + if (busEvent.type) { + track.forEach((event) => { + execute(`${event}_${busEvent.type}`); + }); + } + }, + }); + } + if (toCounts.expand) { + isExpanded({ + execute: count, + track: toCounts.expand, + special: ({ execute, track, busEvent }) => { + if (busEvent.type) { + track.forEach((event) => { + execute(`${event}_${busEvent.type}`); + }); + } + }, + }); + } + + if (toHll.clickFullReport) { + fullReportIsClicked({ execute: user, track: toHll.clickFullReport }); + } + if (toCounts.clickFullReport) { + fullReportIsClicked({ execute: count, track: toCounts.clickFullReport }); + } +} + +function baseTelemetry(componentName) { + const simpleExtensionName = simplifyWidgetName(componentName); + const additionalNonStandard = nonStandardEvents[simpleExtensionName] || {}; + /* + * Telemetry config format is: + * { + * TELEMETRY_TYPE: { + * BEHAVIOR: [ EVENT_NAME, ... ] + * } + * } + * + * Right now, there are currently configurations for these telemetry types: + * - uniqueUser is sent to RedisHLL + * - counter is sent to a regular Redis counter + */ + const defaultTelemetry = { + uniqueUser: { + view: [`${baseRedisEventName(simpleExtensionName)}_view`], + expand: [`${baseRedisEventName(simpleExtensionName)}_expand`], + clickFullReport: [`${baseRedisEventName(simpleExtensionName)}_click_full_report`], + }, + counter: { + view: [`${baseRedisEventName(simpleExtensionName)}_count_view`], + expand: [`${baseRedisEventName(simpleExtensionName)}_count_expand`], + clickFullReport: [`${baseRedisEventName(simpleExtensionName)}_count_click_full_report`], + }, + }; + + return { + uniqueUser: { + view: combineDeepArray('uniqueUser.view', defaultTelemetry, additionalNonStandard), + expand: combineDeepArray('uniqueUser.expand', defaultTelemetry, additionalNonStandard), + clickFullReport: combineDeepArray( + 'uniqueUser.clickFullReport', + defaultTelemetry, + additionalNonStandard, + ), + }, + counter: { + view: combineDeepArray('counter.view', defaultTelemetry, additionalNonStandard), + expand: combineDeepArray('counter.expand', defaultTelemetry, additionalNonStandard), + clickFullReport: combineDeepArray( + 'counter.clickFullReport', + defaultTelemetry, + additionalNonStandard, + ), + }, + }; +} + +export function createTelemetryHub(componentName) { + const bus = createEventHub(); + const config = baseTelemetry(componentName); + + defaultBehaviorEvents({ bus, config }); + + return { + viewed() { + bus.$emit(TELEMETRY_WIDGET_VIEWED); + }, + expanded({ type }) { + bus.$emit(TELEMETRY_WIDGET_EXPANDED, { type }); + }, + fullReportClicked() { + bus.$emit(TELEMETRY_WIDGET_FULL_REPORT_CLICKED); + }, + /* I want a Record here: #{ ...config } // and then the comment would be: This is for debugging only, changing your reference to it does nothing. 😘 */ + config: Object.freeze({ ...config }), // This is *intended* to be for debugging only, but it's pretty mutable, and it has references to live data in child props + bus, + }; +} diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue index f5667aee15b..f8d2732b385 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_collapsible_extension.vue @@ -32,7 +32,7 @@ export default { computed: { arrowIconName() { - return this.isCollapsed ? 'angle-right' : 'angle-down'; + return this.isCollapsed ? 'chevron-lg-right' : 'chevron-lg-down'; }, ariaLabel() { return this.isCollapsed ? __('Expand') : __('Collapse'); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue deleted file mode 100644 index e1d88099580..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue +++ /dev/null @@ -1,100 +0,0 @@ -<script> -import { - GlLink, - GlTooltipDirective, - GlModalDirective, - GlSafeHtmlDirective as SafeHtml, - GlSprintf, -} from '@gitlab/ui'; -import { constructWebIDEPath } from '~/lib/utils/url_utility'; -import { s__ } from '~/locale'; -import clipboardButton from '~/vue_shared/components/clipboard_button.vue'; -import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; -import MrWidgetIcon from './mr_widget_icon.vue'; - -export default { - name: 'MRWidgetHeader', - components: { - clipboardButton, - TooltipOnTruncate, - MrWidgetIcon, - GlLink, - GlSprintf, - }, - directives: { - GlTooltip: GlTooltipDirective, - GlModalDirective, - SafeHtml, - }, - props: { - mr: { - type: Object, - required: true, - }, - }, - computed: { - shouldShowCommitsBehindText() { - return this.mr.divergedCommitsCount > 0; - }, - branchNameClipboardData() { - // This supports code in app/assets/javascripts/copy_to_clipboard.js that - // works around ClipboardJS limitations to allow the context-specific - // copy/pasting of plain text or GFM. - return JSON.stringify({ - text: this.mr.sourceBranch, - gfm: `\`${this.mr.sourceBranch}\``, - }); - }, - webIdePath() { - return constructWebIDEPath(this.mr); - }, - isFork() { - return this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath; - }, - }, - i18n: { - webIdeText: s__('mrWidget|Open in Web IDE'), - gitpodText: s__('mrWidget|Open in Gitpod'), - }, -}; -</script> -<template> - <div class="gl-display-flex mr-source-target"> - <mr-widget-icon name="git-merge" /> - <div class="git-merge-container d-flex"> - <div class="normal"> - <strong> - {{ s__('mrWidget|Request to merge') }} - <tooltip-on-truncate - v-safe-html="mr.sourceBranchLink" - :title="mr.sourceBranch" - truncate-target="child" - class="label-branch label-truncate js-source-branch" - /><clipboard-button - data-testid="mr-widget-copy-clipboard" - :text="branchNameClipboardData" - :title="__('Copy branch name')" - category="tertiary" - /> - {{ s__('mrWidget|into') }} - <tooltip-on-truncate - :title="mr.targetBranch" - truncate-target="child" - class="label-branch label-truncate" - > - <a :href="mr.targetBranchTreePath" class="js-target-branch"> {{ mr.targetBranch }} </a> - </tooltip-on-truncate> - </strong> - <div v-if="shouldShowCommitsBehindText" class="diverged-commits-count"> - <gl-sprintf :message="s__('mrWidget|The source branch is %{link} the target branch')"> - <template #link> - <gl-link :href="mr.targetBranchPath">{{ - n__('%d commit behind', '%d commits behind', mr.divergedCommitsCount) - }}</gl-link> - </template> - </gl-sprintf> - </div> - </div> - </div> - </div> -</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue index 472df8e3110..437342bf438 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue @@ -13,7 +13,7 @@ export default { </script> <template> - <div class="circle-icon-container gl-mr-3 align-self-start align-self-lg-center"> + <div class="circle-icon-container gl-mr-3 align-self-start"> <gl-icon :name="name" :size="24" /> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index 3b3b46e9772..1e1a2049414 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -172,7 +172,7 @@ export default { </p> </template> <template v-else-if="!hasPipeline"> - <gl-loading-icon size="md" /> + <gl-loading-icon size="lg" /> <p class="gl-flex-grow-1 gl-display-flex gl-ml-3 gl-mb-0" data-testid="monitoring-pipeline-message" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue index b8a1f89d232..913aa0e1e34 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_related_links.vue @@ -93,9 +93,7 @@ export default { </span> </p> <div - v-if=" - divergedCommitsCount > 0 && glFeatures.updatedMrHeader && !glFeatures.restructuredMrWidget - " + v-if="divergedCommitsCount > 0 && !glFeatures.restructuredMrWidget" class="diverged-commits-count" > <gl-sprintf :message="s__('mrWidget|The source branch is %{link} the target branch')"> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue index 8b410926c46..45958d7fb8d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue @@ -113,7 +113,7 @@ export default { data-testid="ok" category="primary" class="gl-mt-2" - variant="info" + variant="confirm" :href="pipelinePath" :data-track-property="humanAccess" :data-track-value="$options.SP_SHOW_TRACK_VALUE" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index 4fb95fe635c..cf482410bef 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -417,6 +417,7 @@ export default { } this.isMakingRequest = true; + this.editCommitMessage = false; if (!useAutoMerge) { this.mr.transitionStateMachine({ transition: MERGE }); @@ -663,7 +664,11 @@ export default { <gl-sprintf v-else :message="mergeDisabledText" /> </div> <template v-if="glFeatures.restructuredMrWidget"> - <div v-show="editCommitMessage" class="gl-w-full gl-order-n1"> + <div + v-if="editCommitMessage" + class="gl-w-full gl-order-n1" + data-testid="edit_commit_message" + > <ul :class="{ 'content-list': !glFeatures.restructuredMrWidget, @@ -711,15 +716,13 @@ export default { <div v-if="!restructuredWidgetShowMergeButtons" class="gl-w-full gl-order-n1 gl-text-gray-500" + data-qa-selector="merged_status_content" > <strong v-if="mr.state !== 'closed'"> {{ __('Merge details') }} </strong> <ul class="gl-pl-4 gl-m-0"> - <li - v-if="mr.divergedCommitsCount > 0 && glFeatures.updatedMrHeader" - class="gl-line-height-normal" - > + <li v-if="mr.divergedCommitsCount > 0" class="gl-line-height-normal"> <gl-sprintf :message="s__('mrWidget|The source branch is %{link} the target branch')" > @@ -788,11 +791,7 @@ export default { </div> </div> <template v-if="shouldShowMergeControls && !glFeatures.restructuredMrWidget"> - <div - v-if="!shouldShowMergeEdit" - class="mr-fast-forward-message" - data-qa-selector="fast_forward_message_content" - > + <div v-if="!shouldShowMergeEdit" class="mr-fast-forward-message"> {{ __('Fast-forward merge without a merge commit') }} </div> <commits-header diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue index e0e19094c40..5bd7745d704 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue @@ -126,6 +126,7 @@ export default { }) => { toast(__('Marked as ready. Merging is now allowed.')); $('.merge-request .detail-page-description .title').text(title); + eventHub.$emit('MRWidgetUpdateRequested'); }, ) .catch(() => diff --git a/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue index 2ba945a3ecf..18fdb29ba54 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/terraform/mr_widget_terraform_container.vue @@ -1,5 +1,5 @@ <script> -import { GlDeprecatedSkeletonLoading as GlSkeletonLoading, GlSprintf } from '@gitlab/ui'; +import { GlSkeletonLoader, GlSprintf } from '@gitlab/ui'; import axios from '~/lib/utils/axios_utils'; import Poll from '~/lib/utils/poll'; import { n__ } from '~/locale'; @@ -9,7 +9,7 @@ import TerraformPlan from './terraform_plan.vue'; export default { name: 'MRWidgetTerraformContainer', components: { - GlSkeletonLoading, + GlSkeletonLoader, GlSprintf, MrWidgetExpanableSection, TerraformPlan, @@ -100,7 +100,7 @@ export default { <template> <section class="mr-widget-section"> <div v-if="loading" class="mr-widget-body"> - <gl-skeleton-loading /> + <gl-skeleton-loader /> </div> <mr-widget-expanable-section v-else> |