diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-22 03:10:40 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-22 03:10:40 +0300 |
commit | e3736bd03e55076fb8d9f5b4cc4602b32d13b2ec (patch) | |
tree | dd15325d34dd9bdb5fe37570f1ae691e6e329b7c /app/assets | |
parent | a3e6d34643e760d1a8b8bd1e7e32d8d74c1ea678 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
28 files changed, 336 insertions, 56 deletions
diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue index 9a7296b6b1f..3b71e39d69b 100644 --- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue +++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue @@ -158,7 +158,7 @@ export default { :aria-label="$options.i18n.revokeButton" :data-confirm="modalMessage(name)" data-confirm-btn-variant="danger" - data-qa-selector="revoke_button" + data-testid="revoke-button" data-method="put" :href="revokePath" icon="remove" diff --git a/app/assets/javascripts/access_tokens/components/expires_at_field.vue b/app/assets/javascripts/access_tokens/components/expires_at_field.vue index 38501d63d3a..65206670a3c 100644 --- a/app/assets/javascripts/access_tokens/components/expires_at_field.vue +++ b/app/assets/javascripts/access_tokens/components/expires_at_field.vue @@ -68,7 +68,7 @@ export default { :input-name="inputAttrs.name" :input-id="inputAttrs.id" :placeholder="inputAttrs.placeholder" - data-qa-selector="expiry_date_field" + data-testid="expiry-date-field" /> <template #description> <template v-if="description"> diff --git a/app/assets/javascripts/access_tokens/components/new_access_token_app.vue b/app/assets/javascripts/access_tokens/components/new_access_token_app.vue index 4b51b4333aa..f476503c091 100644 --- a/app/assets/javascripts/access_tokens/components/new_access_token_app.vue +++ b/app/assets/javascripts/access_tokens/components/new_access_token_app.vue @@ -45,7 +45,7 @@ export default { formInputGroupProps() { return { id: this.$options.tokenInputId, - 'data-qa-selector': 'created_access_token_field', + 'data-testid': 'created-access-token-field', name: this.$options.tokenInputId, }; }, @@ -110,7 +110,7 @@ export default { @[$options.EVENT_ERROR]="onError" @[$options.EVENT_SUCCESS]="onSuccess" > - <div ref="container" data-testid="access-token-section" data-qa-selector="access_token_section"> + <div ref="container" data-testid="access-token-section"> <gl-alert v-if="newToken" variant="success" diff --git a/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue index d3b914ea8aa..240bf005532 100644 --- a/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue +++ b/app/assets/javascripts/authentication/two_factor_auth/components/recovery_codes.vue @@ -115,14 +115,10 @@ export default { </gl-sprintf> </p> - <gl-card - class="codes-to-print gl-my-5" - data-testid="recovery-codes" - data-qa-selector="codes_content" - > + <gl-card class="codes-to-print gl-my-5" data-testid="recovery-codes"> <ul class="gl-m-0 gl-pl-5"> <li v-for="(code, index) in codes" :key="index"> - <span class="gl-font-monospace" data-qa-selector="code_content">{{ code }}</span> + <span class="gl-font-monospace" data-testid="code-content">{{ code }}</span> </li> </ul> </gl-card> @@ -131,7 +127,7 @@ export default { <clipboard-button :title="$options.i18n.copyButton" :text="codesAsString" - data-qa-selector="copy_button" + data-testid="copy-button" @click="handleButtonClick($options.copyButtonAction)" > {{ $options.i18n.copyButton }} @@ -163,7 +159,7 @@ export default { :disabled="proceedButtonDisabled" :title="$options.i18n.proceedButton" variant="confirm" - data-qa-selector="proceed_button" + data-testid="proceed-button" data-track-action="click_button" :data-track-label="`${$options.trackingLabelPrefix}proceed_button`" >{{ $options.i18n.proceedButton }}</gl-button diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue index 19da1253a17..0645ffa35dc 100644 --- a/app/assets/javascripts/badges/components/badge_form.vue +++ b/app/assets/javascripts/badges/components/badge_form.vue @@ -183,9 +183,7 @@ export default { }) .catch((error) => { createAlert({ - message: s__( - 'Badges|Adding the badge failed, please check the entered URLs and try again.', - ), + message: s__('Badges|Failed to add new badge. Check the URLs, then try again.'), }); throw error; }); diff --git a/app/assets/javascripts/badges/components/badge_list.vue b/app/assets/javascripts/badges/components/badge_list.vue index 12c9662b30d..a4f88067fa9 100644 --- a/app/assets/javascripts/badges/components/badge_list.vue +++ b/app/assets/javascripts/badges/components/badge_list.vue @@ -29,10 +29,8 @@ export default { GlModal: GlModalDirective, }, i18n: { - emptyGroupMessage: s__('Badges|This group has no badges, start by creating a new one above.'), - emptyProjectMessage: s__( - 'Badges|This project has no badges, start by creating a new one above.', - ), + emptyGroupMessage: s__('Badges|This group has no badges. Add an existing badge or create one.'), + emptyProjectMessage: s__('Badges|This project has no badges. Start by adding a new badge.'), }, data() { return { diff --git a/app/assets/javascripts/badges/components/badge_settings.vue b/app/assets/javascripts/badges/components/badge_settings.vue index f0d354c6378..32c24564d21 100644 --- a/app/assets/javascripts/badges/components/badge_settings.vue +++ b/app/assets/javascripts/badges/components/badge_settings.vue @@ -25,7 +25,7 @@ export default { addButton: s__('Badges|Add badge'), addFormTitle: s__('Badges|Add new badge'), deleteModalText: s__( - 'Badges|You are going to delete this badge. Deleted badges %{strongStart}cannot%{strongEnd} be restored.', + 'Badges|If you delete this badge, you %{strongStart}cannot%{strongEnd} restore it.', ), }, data() { @@ -74,7 +74,7 @@ export default { }) .catch((error) => { createAlert({ - message: s__('Badges|Deleting the badge failed, please try again.'), + message: s__('Badges|Failed to delete the badge. Try again.'), }); throw error; }); diff --git a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue index b37333d923c..7f76b7ca1ac 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_trigger.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_trigger.vue @@ -4,7 +4,6 @@ import { s__ } from '~/locale'; import eventHub from '../event_hub'; import { TRIGGER_ELEMENT_BUTTON, - TRIGGER_DEFAULT_TESTID, TRIGGER_ELEMENT_WITH_EMOJI, TRIGGER_ELEMENT_DROPDOWN_WITH_EMOJI, TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN, @@ -42,17 +41,12 @@ export default { required: false, default: 'button', }, - testId: { - type: String, - required: false, - default: TRIGGER_DEFAULT_TESTID, - }, }, computed: { componentAttributes() { return { class: this.classes, - 'data-testid': this.testId, + 'data-testid': 'invite-members-button', }; }, item() { diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js index f44313ff41f..3b2840ecf11 100644 --- a/app/assets/javascripts/invite_members/constants.js +++ b/app/assets/javascripts/invite_members/constants.js @@ -20,7 +20,6 @@ export const TRIGGER_ELEMENT_WITH_EMOJI = 'text-emoji'; export const TRIGGER_ELEMENT_DROPDOWN_WITH_EMOJI = 'dropdown-text-emoji'; export const TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN = 'dropdown-text'; export const INVITE_MEMBER_MODAL_TRACKING_CATEGORY = 'invite_members_modal'; -export const TRIGGER_DEFAULT_TESTID = 'invite-members-button'; export const IMPORT_PROJECT_MEMBERS_MODAL_TRACKING_CATEGORY = 'invite_project_members_modal'; export const IMPORT_PROJECT_MEMBERS_MODAL_TRACKING_LABEL = 'project-members-page'; export const MEMBERS_MODAL_DEFAULT_TITLE = s__('InviteMembersModal|Invite members'); diff --git a/app/assets/javascripts/ml/model_registry/apps/show_ml_model.vue b/app/assets/javascripts/ml/model_registry/apps/show_ml_model.vue index e8ec8f157ef..2c60e05dc57 100644 --- a/app/assets/javascripts/ml/model_registry/apps/show_ml_model.vue +++ b/app/assets/javascripts/ml/model_registry/apps/show_ml_model.vue @@ -2,11 +2,13 @@ import { GlTab, GlTabs, GlBadge } from '@gitlab/ui'; import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue'; import TitleArea from '~/vue_shared/components/registry/title_area.vue'; +import ModelVersionList from '~/ml/model_registry/components/model_version_list.vue'; import * as i18n from '../translations'; export default { name: 'ShowMlModelApp', components: { + ModelVersionList, TitleArea, GlTabs, GlTab, @@ -59,6 +61,8 @@ export default { {{ $options.i18n.MODEL_OTHER_VERSIONS_TAB_LABEL }} <gl-badge size="sm" class="gl-tab-counter-badge">{{ versionCount }}</gl-badge> </template> + + <model-version-list :model-id="model.id" /> </gl-tab> <gl-tab> <template #title> diff --git a/app/assets/javascripts/ml/model_registry/components/model_version_list.vue b/app/assets/javascripts/ml/model_registry/components/model_version_list.vue new file mode 100644 index 00000000000..bf036b1085e --- /dev/null +++ b/app/assets/javascripts/ml/model_registry/components/model_version_list.vue @@ -0,0 +1,143 @@ +<script> +import { GlAlert } from '@gitlab/ui'; +import { n__ } from '~/locale'; +import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue'; +import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue'; +import * as Sentry from '~/sentry/sentry_browser_wrapper'; +import { + FAILED_TO_LOAD_MODEL_VERSIONS_MESSAGE, + NO_VERSIONS_LABEL, +} from '~/ml/model_registry/translations'; +import { convertToGraphQLId } from '~/graphql_shared/utils'; +import getModelVersionsQuery from '../graphql/queries/get_model_versions.query.graphql'; +import { GRAPHQL_PAGE_SIZE } from '../constants'; +import ModelVersionRow from './model_version_row.vue'; + +export default { + components: { + GlAlert, + ModelVersionRow, + PackagesListLoader, + RegistryList, + }, + props: { + modelId: { + type: Number, + required: true, + }, + }, + data() { + return { + modelVersions: {}, + fetchModelVersionsError: false, + }; + }, + apollo: { + modelVersions: { + query: getModelVersionsQuery, + variables() { + return this.queryVariables; + }, + update(data) { + return data.mlModel?.versions ?? {}; + }, + error(error) { + this.fetchModelVersionsError = true; + Sentry.captureException(error); + }, + }, + }, + computed: { + gid() { + return convertToGraphQLId('Ml::Model', this.modelId); + }, + isListEmpty() { + return this.count === 0; + }, + isLoading() { + return this.$apollo.queries.modelVersions.loading; + }, + pageInfo() { + return this.modelVersions?.pageInfo ?? {}; + }, + listTitle() { + return n__('%d version', '%d versions', this.versions.length); + }, + queryVariables() { + return { + id: this.gid, + first: GRAPHQL_PAGE_SIZE, + }; + }, + versions() { + return this.modelVersions?.nodes ?? []; + }, + count() { + return this.modelVersions?.count ?? 0; + }, + }, + methods: { + fetchPreviousVersionsPage() { + const variables = { + ...this.queryVariables, + first: null, + last: GRAPHQL_PAGE_SIZE, + before: this.pageInfo?.startCursor, + }; + this.$apollo.queries.modelVersions.fetchMore({ + variables, + updateQuery: (previousResult, { fetchMoreResult }) => { + return fetchMoreResult; + }, + }); + }, + fetchNextVersionsPage() { + const variables = { + ...this.queryVariables, + first: GRAPHQL_PAGE_SIZE, + last: null, + after: this.pageInfo?.endCursor, + }; + + this.$apollo.queries.modelVersions.fetchMore({ + variables, + updateQuery: (previousResult, { fetchMoreResult }) => { + return fetchMoreResult; + }, + }); + }, + }, + i18n: { + FAILED_TO_LOAD_MODEL_VERSIONS_MESSAGE, + NO_VERSIONS_LABEL, + }, +}; +</script> +<template> + <div> + <div v-if="isLoading"> + <packages-list-loader /> + </div> + <gl-alert v-else-if="fetchModelVersionsError" variant="danger" :dismissible="false">{{ + $options.i18n.FAILED_TO_LOAD_MODEL_VERSIONS_MESSAGE + }}</gl-alert> + <div v-else-if="isListEmpty" class="gl-text-secondary"> + {{ $options.i18n.NO_VERSIONS_LABEL }} + </div> + <div v-else> + <registry-list + :hidden-delete="true" + :is-loading="isLoading" + :items="versions" + :pagination="pageInfo" + :title="listTitle" + @prev-page="fetchPreviousVersionsPage" + @next-page="fetchNextVersionsPage" + > + <template #default="{ item }"> + <model-version-row :model-version="item" /> + </template> + </registry-list> + </div> + </div> +</template> diff --git a/app/assets/javascripts/ml/model_registry/components/model_version_row.vue b/app/assets/javascripts/ml/model_registry/components/model_version_row.vue new file mode 100644 index 00000000000..7e024ff546d --- /dev/null +++ b/app/assets/javascripts/ml/model_registry/components/model_version_row.vue @@ -0,0 +1,49 @@ +<script> +import { GlLink, GlSprintf, GlTruncate } from '@gitlab/ui'; +import ListItem from '~/vue_shared/components/registry/list_item.vue'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; + +export default { + name: 'MlModelVersionRow', + components: { + ListItem, + GlLink, + GlTruncate, + GlSprintf, + TimeAgoTooltip, + }, + props: { + modelVersion: { + type: Object, + required: true, + }, + }, + computed: { + pathToDetails() { + return this.modelVersion._links?.showPath; + }, + }, +}; +</script> + +<template> + <list-item v-bind="$attrs"> + <template #left-primary> + <div class="gl-display-flex gl-align-items-center"> + <gl-link class="gl-text-body" :href="pathToDetails"> + <gl-truncate :text="modelVersion.version" /> + </gl-link> + </div> + </template> + + <template #left-secondary> + <span> + <gl-sprintf :message="__('Created %{timestamp}')"> + <template #timestamp> + <time-ago-tooltip :time="modelVersion.createdAt" /> + </template> + </gl-sprintf> + </span> + </template> + </list-item> +</template> diff --git a/app/assets/javascripts/ml/model_registry/constants.js b/app/assets/javascripts/ml/model_registry/constants.js index 10c21ec4f12..7b94264dfd5 100644 --- a/app/assets/javascripts/ml/model_registry/constants.js +++ b/app/assets/javascripts/ml/model_registry/constants.js @@ -11,3 +11,5 @@ export const BASE_SORT_FIELDS = Object.freeze([ label: s__('MlExperimentTracking|Created at'), }, ]); + +export const GRAPHQL_PAGE_SIZE = 30; diff --git a/app/assets/javascripts/ml/model_registry/graphql/queries/get_model_versions.query.graphql b/app/assets/javascripts/ml/model_registry/graphql/queries/get_model_versions.query.graphql new file mode 100644 index 00000000000..1b48a67a0bd --- /dev/null +++ b/app/assets/javascripts/ml/model_registry/graphql/queries/get_model_versions.query.graphql @@ -0,0 +1,22 @@ +query getModelVersions($id: MlModelID!, $first: Int, $last: Int, $after: String, $before: String) { + mlModel(id: $id) { + id + versions(after: $after, before: $before, first: $first, last: $last) { + count + nodes { + id + version + createdAt + _links { + showPath + } + } + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + } + } +} diff --git a/app/assets/javascripts/ml/model_registry/translations.js b/app/assets/javascripts/ml/model_registry/translations.js index 689eeb45b00..bbafde0b943 100644 --- a/app/assets/javascripts/ml/model_registry/translations.js +++ b/app/assets/javascripts/ml/model_registry/translations.js @@ -32,3 +32,6 @@ export const CI_SECTION_LABEL = s__('MlModelRegistry|CI Info'); export const JOB_LABEL = __('Job'); export const CI_USER_LABEL = s__('MlModelRegistry|Triggered by'); export const CI_MR_LABEL = __('Merge request'); +export const FAILED_TO_LOAD_MODEL_VERSIONS_MESSAGE = s__( + 'MlModelRegistry|Failed to load model versions', +); diff --git a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js index 8fe822e4639..41952a33c05 100644 --- a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js +++ b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js @@ -8,7 +8,7 @@ const skippable = twoFactorNode ? parseBoolean(twoFactorNode.dataset.twoFactorSk if (skippable) { const button = `<div class="gl-alert-actions"> - <a class="btn gl-button btn-md btn-confirm gl-alert-action" data-qa-selector="configure_it_later_button" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a> + <a class="btn gl-button btn-md btn-confirm gl-alert-action" data-testid="configure-it-later-button" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a> </div>`; const flashAlert = document.querySelector('.flash-alert'); if (flashAlert) { diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue index 915f6578ac3..e9a67a401b8 100644 --- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue +++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue @@ -42,7 +42,7 @@ export default { text: __('Delete account'), attributes: { variant: 'danger', - 'data-qa-selector': 'confirm_delete_account_button', + 'data-testid': 'confirm-delete-account-button', category: 'primary', disabled: !this.canSubmit, }, @@ -128,7 +128,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), name="password" class="form-control" type="password" - data-qa-selector="password_confirmation_field" + data-testid="password-confirmation-field" aria-labelledby="input-label" /> <input diff --git a/app/assets/javascripts/super_sidebar/components/user_menu.vue b/app/assets/javascripts/super_sidebar/components/user_menu.vue index 2823aeaee7b..db769df873f 100644 --- a/app/assets/javascripts/super_sidebar/components/user_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/user_menu.vue @@ -97,7 +97,7 @@ export default { text: this.$options.i18n.editProfile, href: this.data.settings.profile_path, extraAttrs: { - 'data-testid': 'edit_profile_link', + 'data-testid': 'edit-profile-link', ...USER_MENU_TRACKING_DEFAULTS, 'data-track-label': 'user_edit_profile', }, @@ -242,7 +242,7 @@ export default { :entity-name="data.name" :src="data.avatar_url" aria-hidden="true" - data-testid="user_avatar_content" + data-testid="user-avatar-content" /> <span v-if="showNotificationDot" diff --git a/app/assets/javascripts/terms/components/app.vue b/app/assets/javascripts/terms/components/app.vue index 29099bcc366..75ee0e16d4e 100644 --- a/app/assets/javascripts/terms/components/app.vue +++ b/app/assets/javascripts/terms/components/app.vue @@ -66,7 +66,7 @@ export default { <template> <div> - <div class="gl-relative gl-pb-0 gl-px-0" data-qa-selector="terms_content"> + <div class="gl-relative gl-pb-0 gl-px-0" data-testid="terms-content"> <div class="terms-fade gl-absolute gl-left-5 gl-right-5 gl-bottom-0 gl-h-11 gl-pointer-events-none" ></div> @@ -97,7 +97,7 @@ export default { type="submit" variant="confirm" :disabled="acceptDisabled" - data-qa-selector="accept_terms_button" + data-testid="accept-terms-button" >{{ $options.i18n.accept }}</gl-button > <input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue index ac403c2c6f2..750f53a29b6 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue @@ -1,9 +1,12 @@ <script> import { GlSkeletonLoader } from '@gitlab/ui'; import { __, n__, sprintf } from '~/locale'; +import { TYPENAME_MERGE_REQUEST } from '~/graphql_shared/constants'; +import { convertToGraphQLId } from '~/graphql_shared/utils'; import { COMPONENTS } from '~/vue_merge_request_widget/components/checks/constants'; import mergeRequestQueryVariablesMixin from '../mixins/merge_request_query_variables'; import mergeChecksQuery from '../queries/merge_checks.query.graphql'; +import mergeChecksSubscription from '../queries/merge_checks.subscription.graphql'; import StateContainer from './state_container.vue'; import BoldText from './bold_text.vue'; @@ -18,6 +21,31 @@ export default { return this.mergeRequestQueryVariables; }, update: (data) => data?.project?.mergeRequest, + subscribeToMore: { + document() { + return mergeChecksSubscription; + }, + skip() { + return !this.mr?.id; + }, + variables() { + return { + issuableId: convertToGraphQLId(TYPENAME_MERGE_REQUEST, this.mr?.id), + }; + }, + updateQuery( + _, + { + subscriptionData: { + data: { mergeRequestMergeStatusUpdated }, + }, + }, + ) { + if (mergeRequestMergeStatusUpdated) { + this.state = mergeRequestMergeStatusUpdated; + } + }, + }, }, }, components: { @@ -86,7 +114,7 @@ export default { </script> <template> - <div> + <div class="gl-rounded-0!"> <state-container :is-loading="isLoading" :status="statusIcon" diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue index 122abc7d034..8fb2b6acc4d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue @@ -50,7 +50,6 @@ export default { { text: s__('mrWidget|Refresh now'), onClick: () => this.refresh(), - testId: 'merge-request-failed-refresh-button', }, ]; }, diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index cc116b42f1e..e2f301b1911 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -54,6 +54,7 @@ import getStateQuery from './queries/get_state.query.graphql'; import getStateSubscription from './queries/get_state.subscription.graphql'; import ReportWidgetContainer from './components/report_widget_container.vue'; import MrWidgetReadyToMerge from './components/states/new_ready_to_merge.vue'; +import MergeChecks from './components/merge_checks.vue'; export default { // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25 @@ -94,6 +95,7 @@ export default { MergeChecksFailed: () => import('./components/states/merge_checks_failed.vue'), ReadyToMerge: ReadyToMergeState, ReportWidgetContainer, + MergeChecks, }, apollo: { state: { @@ -248,6 +250,25 @@ export default { hasExtensions() { return registeredExtensions.extensions.length; }, + mergeBlockedComponentEnabled() { + return ( + window.gon?.features?.mergeBlockedComponent && + !( + [ + 'checking', + 'preparing', + 'nothingToMerge', + 'archived', + 'missingBranch', + 'merged', + 'closed', + 'merging', + 'autoMergeEnabled', + 'shaMismatch', + ].includes(this.mr.state) || ['MERGING', 'AUTO_MERGE'].includes(this.mr.machineValue) + ) + ); + }, }, watch: { 'mr.machineValue': { @@ -556,7 +577,8 @@ export default { </div> <div class="mr-widget-section" data-testid="mr-widget-content"> - <component :is="componentName" :mr="mr" :service="service" /> + <merge-checks v-if="mergeBlockedComponentEnabled" :mr="mr" :service="service" /> + <component :is="componentName" v-else :mr="mr" :service="service" /> <ready-to-merge v-if="mr.commitsCount" v-show="shouldShowMergeDetails" diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/merge_checks.subscription.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/merge_checks.subscription.graphql new file mode 100644 index 00000000000..9cf2b9be405 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/queries/merge_checks.subscription.graphql @@ -0,0 +1,14 @@ +subscription mergeChecksSubscrption($issuableId: IssuableID!) { + mergeRequestMergeStatusUpdated(issuableId: $issuableId) { + ... on MergeRequest { + id + userPermissions { + canMerge + } + mergeabilityChecks { + identifier + status + } + } + } +} diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js index d6bab074f3f..5765d7a56fa 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/get_state_key.js @@ -15,25 +15,25 @@ export default function deviseState() { return stateKey.missingBranch; } if (this.detailedMergeStatus === DETAILED_MERGE_STATUS.CHECKING) { - return stateKey.checking; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.checking; } if (this.hasConflicts) { - return stateKey.conflicts; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.conflicts; } if (this.shouldBeRebased) { - return stateKey.rebase; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.rebase; } if (this.hasMergeChecksFailed && !this.autoMergeEnabled) { - return stateKey.mergeChecksFailed; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.mergeChecksFailed; } if (this.detailedMergeStatus === DETAILED_MERGE_STATUS.CI_MUST_PASS) { - return stateKey.pipelineFailed; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.pipelineFailed; } if (this.detailedMergeStatus === DETAILED_MERGE_STATUS.DRAFT_STATUS) { - return stateKey.draft; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.draft; } if (this.detailedMergeStatus === DETAILED_MERGE_STATUS.DISCUSSIONS_NOT_RESOLVED) { - return stateKey.unresolvedDiscussions; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.unresolvedDiscussions; } if (this.canMerge && this.isSHAMismatch) { return stateKey.shaMismatch; @@ -47,5 +47,5 @@ export default function deviseState() { ) { return stateKey.readyToMerge; } - return stateKey.checking; + return window.gon?.features?.mergeBlockedComponent ? null : stateKey.checking; } diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index a1b86c86979..473e8a936fd 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -30,7 +30,8 @@ export default class MergeRequestStore { this.stateMachine = machine(STATE_MACHINE.definition); this.machineValue = this.stateMachine.value; - this.mergeDetailsCollapsed = window.innerWidth < 768; + this.mergeDetailsCollapsed = + !window.gon?.features?.mergeBlockedComponent && window.innerWidth < 768; this.mergeError = data.mergeError; this.multipleApprovalRulesAvailable = data.multiple_approval_rules_available || false; this.id = data.id; @@ -196,7 +197,9 @@ export default class MergeRequestStore { } this.commitsCount = mergeRequest.commitCount; - this.branchMissing = !mergeRequest.sourceBranchExists || !mergeRequest.targetBranchExists; + this.branchMissing = + mergeRequest.detailedMergeStatus !== 'NOT_OPEN' && + (!mergeRequest.sourceBranchExists || !mergeRequest.targetBranchExists); this.hasConflicts = mergeRequest.conflicts; this.hasMergeableDiscussionsState = mergeRequest.mergeableDiscussionsState === false; this.mergeError = mergeRequest.mergeError; @@ -418,6 +421,10 @@ export default class MergeRequestStore { } toggleMergeDetails(val = !this.mergeDetailsCollapsed) { + if (window.gon?.features?.mergeBlockedComponent) { + return; + } + this.mergeDetailsCollapsed = val; } } diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue index a1ef1f30ebb..5019ab901fd 100644 --- a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue +++ b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue @@ -72,7 +72,7 @@ export default { attributes: { variant: 'danger', disabled: !this.isValid, - 'data-qa-selector': 'confirm_danger_modal_button', + 'data-testid': 'confirm-danger-modal-button', }, }; }, @@ -133,8 +133,7 @@ export default { id="confirm_name_input" v-model="confirmationPhrase" class="form-control" - data-qa-selector="confirm_danger_field" - data-testid="confirm-danger-input" + data-testid="confirm-danger-field" type="text" /> </gl-form-group> diff --git a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue index 0455685627d..b03da19a896 100644 --- a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue +++ b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.vue @@ -179,7 +179,6 @@ export default { :aria-label="toggleVisibilityLabel" :icon="toggleVisibilityIcon" data-testid="toggle-visibility-button" - data-qa-selector="toggle_visibility_button" @click.stop="handleToggleVisibilityButtonClick" /> <clipboard-button diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_new.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_new.vue index 3b6dcace8fe..89b64f03e1f 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_new.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_new.vue @@ -66,6 +66,10 @@ export default { const page = getPageParamValue(this.number); return getPageSearchString(this.blamePath, page); }, + codeStyling() { + const defaultGutterWidth = 96; + return { marginLeft: `${this.$refs.lineNumbers?.offsetWidth || defaultGutterWidth}px` }; + }, }, methods: { handleChunkAppear() { @@ -80,7 +84,7 @@ export default { </script> <template> <div class="gl-display-flex"> - <div v-if="shouldHighlight" class="gl-display-flex gl-flex-direction-column"> + <div v-if="shouldHighlight" class="gl-display-flex gl-flex-direction-column gl-absolute"> <div v-for="(n, index) in totalLines" :key="index" @@ -102,14 +106,14 @@ export default { </div> </div> - <div v-else class="line-numbers gl-p-0! gl-mr-3 gl-text-transparent"> + <div v-else ref="lineNumbers" class="line-numbers gl-p-0! gl-mr-3 gl-text-transparent"> <!-- Placeholder for line numbers while content is not highlighted --> </div> <gl-intersection-observer class="gl-w-full" @appear="handleChunkAppear"> <pre class="gl-m-0 gl-p-0! gl-w-full gl-overflow-visible! gl-border-none! code highlight gl-line-height-0" - ><code v-if="shouldHighlight" v-safe-html="highlightedContent" data-testid="content"></code><code v-else v-once class="line gl-white-space-pre-wrap! gl-ml-1" data-testid="content" v-text="rawContent"></code></pre> + ><code v-if="shouldHighlight" v-safe-html="highlightedContent" :style="codeStyling" data-testid="content"></code><code v-else v-once class="line gl-white-space-pre-wrap! gl-ml-1" data-testid="content" v-text="rawContent"></code></pre> </gl-intersection-observer> </div> </template> |