diff options
Diffstat (limited to 'app/assets/javascripts/merge_requests')
6 files changed, 192 insertions, 15 deletions
diff --git a/app/assets/javascripts/merge_requests/components/compare_app.vue b/app/assets/javascripts/merge_requests/components/compare_app.vue index 8e02048f494..c7c16e91e4c 100644 --- a/app/assets/javascripts/merge_requests/components/compare_app.vue +++ b/app/assets/javascripts/merge_requests/components/compare_app.vue @@ -23,9 +23,6 @@ export default { currentProject: { default: () => ({}), }, - currentBranch: { - default: () => ({}), - }, inputs: { default: () => ({}), }, @@ -35,8 +32,12 @@ export default { toggleClass: { default: () => ({}), }, - branchQaSelector: { - default: '', + }, + props: { + currentBranch: { + type: Object, + required: false, + default: () => ({}), }, }, data() { @@ -57,6 +58,12 @@ export default { return this.commitHtml || this.loading || !this.selectedBranch.value; }, }, + watch: { + currentBranch(newVal) { + this.selectedBranch = newVal; + this.fetchCommit(); + }, + }, mounted() { this.fetchCommit(); }, @@ -67,6 +74,7 @@ export default { selectBranch(branch) { this.selectedBranch = branch; this.fetchCommit(); + this.$emit('select-branch', branch.value); }, async fetchCommit() { if (!this.selectedBranch.value) return; @@ -108,7 +116,7 @@ export default { :input-name="inputs.branch.name" :default="currentBranch" :toggle-class="toggleClass.branch" - :qa-selector="branchQaSelector" + data-testid="compare-dropdown" @selected="selectBranch" /> </div> diff --git a/app/assets/javascripts/merge_requests/components/compare_dropdown.vue b/app/assets/javascripts/merge_requests/components/compare_dropdown.vue index a5a4e683214..2855d704507 100644 --- a/app/assets/javascripts/merge_requests/components/compare_dropdown.vue +++ b/app/assets/javascripts/merge_requests/components/compare_dropdown.vue @@ -46,11 +46,6 @@ export default { required: false, default: '', }, - qaSelector: { - type: String, - required: false, - default: null, - }, }, data() { return { @@ -70,6 +65,12 @@ export default { ); }, }, + watch: { + default(newVal) { + this.current = newVal; + this.selected = newVal.value; + }, + }, methods: { async fetchData() { if (!this.endpoint) return; @@ -136,7 +137,7 @@ export default { 'gl-align-items-flex-start! gl-justify-content-start! mr-compare-dropdown', toggleClass, ]" - :data-qa-selector="qaSelector" + data-testid="source-branch-dropdown" @shown="fetchData" @search="searchData" @select="selectItem" diff --git a/app/assets/javascripts/merge_requests/components/header_metadata.vue b/app/assets/javascripts/merge_requests/components/header_metadata.vue new file mode 100644 index 00000000000..fce7ba385b4 --- /dev/null +++ b/app/assets/javascripts/merge_requests/components/header_metadata.vue @@ -0,0 +1,69 @@ +<script> +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; +// eslint-disable-next-line no-restricted-imports +import { mapGetters } from 'vuex'; +import { __ } from '~/locale'; +import { TYPE_ISSUE, WORKSPACE_PROJECT } from '~/issues/constants'; +import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; + +export default { + TYPE_ISSUE, + WORKSPACE_PROJECT, + components: { + GlIcon, + ConfidentialityBadge, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + inject: ['hidden'], + computed: { + ...mapGetters(['getNoteableData']), + isLocked() { + return this.getNoteableData.discussion_locked; + }, + isConfidential() { + return this.getNoteableData.confidential; + }, + warningIconsMeta() { + return [ + { + iconName: 'lock', + visible: this.isLocked, + dataTestId: 'locked', + tooltip: __('This merge request is locked. Only project members can comment.'), + }, + { + iconName: 'spam', + visible: this.hidden, + dataTestId: 'hidden', + tooltip: __('This merge request is hidden because its author has been banned'), + }, + ]; + }, + }, +}; +</script> + +<template> + <div class="gl-display-inline-block"> + <confidentiality-badge + v-if="isConfidential" + class="gl-mr-3" + :issuable-type="$options.TYPE_ISSUE" + :workspace-type="$options.WORKSPACE_PROJECT" + /> + <template v-for="meta in warningIconsMeta"> + <div + v-if="meta.visible" + :key="meta.iconName" + v-gl-tooltip.bottom + :data-testid="meta.dataTestId" + :title="meta.tooltip || null" + class="issuable-warning-icon gl-mr-3 gl-mt-2 gl-display-flex gl-justify-content-center gl-align-items-center" + > + <gl-icon :name="meta.iconName" class="icon" /> + </div> + </template> + </div> +</template> diff --git a/app/assets/javascripts/merge_requests/components/merge_request_status_badge.vue b/app/assets/javascripts/merge_requests/components/merge_request_status_badge.vue new file mode 100644 index 00000000000..3d5478757a8 --- /dev/null +++ b/app/assets/javascripts/merge_requests/components/merge_request_status_badge.vue @@ -0,0 +1,74 @@ +<script> +import Vue from 'vue'; +import { fetchPolicies } from '~/lib/graphql'; +import StatusBadge from '~/issuable/components/status_badge.vue'; + +export const badgeState = Vue.observable({ + state: '', + updateStatus: null, +}); + +export default { + components: { + StatusBadge, + }, + inject: { + query: { default: null }, + projectPath: { default: null }, + iid: { default: null }, + }, + props: { + initialState: { + type: String, + required: false, + default: null, + }, + issuableType: { + type: String, + required: false, + default: '', + }, + }, + data() { + if (!this.iid) { + return { + state: this.initialState, + }; + } + + if (!badgeState.state && this.initialState) { + badgeState.state = this.initialState; + } + + return badgeState; + }, + created() { + if (!badgeState.updateStatus) { + badgeState.updateStatus = this.fetchState; + } + }, + beforeDestroy() { + if (badgeState.updateStatus && this.query) { + badgeState.updateStatus = null; + } + }, + methods: { + async fetchState() { + const { data } = await this.$apollo.query({ + query: this.query, + variables: { + projectPath: this.projectPath, + iid: this.iid, + }, + fetchPolicy: fetchPolicies.NO_CACHE, + }); + + badgeState.state = data?.workspace?.issuable?.state; + }, + }, +}; +</script> + +<template> + <status-badge class="gl-align-self-center gl-mr-3" :issuable-type="issuableType" :state="state" /> +</template> diff --git a/app/assets/javascripts/merge_requests/components/sticky_header.vue b/app/assets/javascripts/merge_requests/components/sticky_header.vue index 3c3bee9b108..c1e88a901c4 100644 --- a/app/assets/javascripts/merge_requests/components/sticky_header.vue +++ b/app/assets/javascripts/merge_requests/components/sticky_header.vue @@ -7,13 +7,15 @@ import { TYPENAME_MERGE_REQUEST } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { isLoggedIn } from '~/lib/utils/common_utils'; -import StatusBox from '~/issuable/components/status_box.vue'; +import StatusBadge from '~/issuable/components/status_badge.vue'; +import { TYPE_MERGE_REQUEST } from '~/issues/constants'; import DiscussionCounter from '~/notes/components/discussion_counter.vue'; import TodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import titleSubscription from '../queries/title.subscription.graphql'; export default { + TYPE_MERGE_REQUEST, apollo: { $subscribe: { title: { @@ -41,8 +43,8 @@ export default { GlLink, GlSprintf, GlBadge, - StatusBox, DiscussionCounter, + StatusBadge, TodoWidget, ClipboardButton, }, @@ -115,7 +117,11 @@ export default { :class="{ 'gl-max-w-container-xl': !isFluidLayout }" > <div class="gl-w-full gl-display-flex gl-align-items-baseline"> - <status-box :initial-state="getNoteableData.state" issuable-type="merge_request" /> + <status-badge + class="gl-align-self-center gl-mr-3" + :issuable-type="$options.TYPE_MERGE_REQUEST" + :state="getNoteableData.state" + /> <a v-safe-html:[$options.safeHtmlConfig]="titleHtml" href="#top" diff --git a/app/assets/javascripts/merge_requests/index.js b/app/assets/javascripts/merge_requests/index.js new file mode 100644 index 00000000000..29218eb53e0 --- /dev/null +++ b/app/assets/javascripts/merge_requests/index.js @@ -0,0 +1,19 @@ +import Vue from 'vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import HeaderMetadata from './components/header_metadata.vue'; + +export function mountHeaderMetadata(store) { + const el = document.querySelector('.js-header-metadata-root'); + + if (!el) { + return null; + } + + return new Vue({ + el, + name: 'HeaderMetadataRoot', + store, + provide: { hidden: parseBoolean(el.dataset.hidden) }, + render: (createElement) => createElement(HeaderMetadata), + }); +} |