diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-19 15:15:59 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-19 15:15:59 +0300 |
commit | 2017bc90a671eac669f0114b6ef508e151409c4f (patch) | |
tree | 79bbbedede417d3ce13ae2e13dd1ad3bb069c975 /app/assets/javascripts | |
parent | 9450a63064cd1572f030628dbf155f5c047f28c7 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
17 files changed, 183 insertions, 178 deletions
diff --git a/app/assets/javascripts/admin/users/components/user_actions.vue b/app/assets/javascripts/admin/users/components/user_actions.vue index e21bb58cb4e..38c7d3f9b90 100644 --- a/app/assets/javascripts/admin/users/components/user_actions.vue +++ b/app/assets/javascripts/admin/users/components/user_actions.vue @@ -61,6 +61,9 @@ export default { hasEditAction() { return this.userActions.includes('edit'); }, + hasEditActionOnly() { + return this.hasEditAction === true && this.hasDeleteActions === false; + }, userPaths() { return generateUserPaths(this.paths, this.user.username); }, @@ -91,10 +94,13 @@ export default { class="gl-display-flex gl-justify-content-end gl-my-n2 gl-mx-n2" :data-testid="`user-actions-${user.id}`" > - <div v-if="hasEditAction" class="gl-p-2"> - <gl-button v-if="showButtonLabels" v-bind="editButtonAttrs" icon="pencil-square">{{ - $options.i18n.edit - }}</gl-button> + <div v-if="hasEditAction" class="gl-p-2" :class="{ 'gl-mr-3': hasEditActionOnly }"> + <gl-button + v-if="showButtonLabels" + v-bind="editButtonAttrs" + :class="{ 'gl-mr-7': hasEditActionOnly }" + >{{ $options.i18n.edit }}</gl-button + > <gl-button v-else v-gl-tooltip="$options.i18n.edit" @@ -106,11 +112,14 @@ export default { <div v-if="hasDropdownActions" class="gl-p-2"> <gl-disclosure-dropdown + icon="ellipsis_v" + category="tertiary" :toggle-text="$options.i18n.userAdministration" + text-sr-only data-testid="dropdown-toggle" data-qa-selector="user_actions_dropdown_toggle" :data-qa-username="user.username" - placement="left" + no-caret > <template v-for="action in dropdownSafeActions"> <component diff --git a/app/assets/javascripts/admin/users/components/users_table.vue b/app/assets/javascripts/admin/users/components/users_table.vue index e55622d40ba..2d2c598f953 100644 --- a/app/assets/javascripts/admin/users/components/users_table.vue +++ b/app/assets/javascripts/admin/users/components/users_table.vue @@ -135,7 +135,7 @@ export default { </template> <template #cell(settings)="{ item: user }"> - <user-actions :user="user" :paths="paths" /> + <user-actions :user="user" :paths="paths" :show-button-labels="true" /> </template> </gl-table> </div> diff --git a/app/assets/javascripts/diffs/components/diff_view.vue b/app/assets/javascripts/diffs/components/diff_view.vue index a2e052e0f93..348d6d1d78d 100644 --- a/app/assets/javascripts/diffs/components/diff_view.vue +++ b/app/assets/javascripts/diffs/components/diff_view.vue @@ -1,7 +1,6 @@ <script> import { mapGetters, mapState, mapActions } from 'vuex'; import { IdState } from 'vendor/vue-virtual-scroller'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DraftNote from '~/batch_comments/components/draft_note.vue'; import draftCommentsMixin from '~/diffs/mixins/draft_comments'; import { getCommentedLines } from '~/notes/components/multiline_comment_utils'; @@ -21,11 +20,7 @@ export default { DiffCommentCell, DraftNote, }, - mixins: [ - draftCommentsMixin, - IdState({ idProp: (vm) => vm.diffFile.file_hash }), - glFeatureFlagsMixin(), - ], + mixins: [draftCommentsMixin, IdState({ idProp: (vm) => vm.diffFile.file_hash })], props: { diffFile: { type: Object, @@ -265,10 +260,7 @@ export default { @stopdragging="onStopDragging" /> <diff-line - v-if=" - glFeatures.refactorCodeQualityInlineFindings && - codeQualityExpandedLines.includes(getCodeQualityLine(line)) - " + v-if="codeQualityExpandedLines.includes(getCodeQualityLine(line))" :key="line.line_code" :line="line" @hideCodeQualityFindings="hideCodeQualityFindings" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue index ffb8c0eb88c..caeee7edefe 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue @@ -1,12 +1,10 @@ <script> import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui'; import Tracking from '~/tracking'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import eventHub from '../../event_hub'; import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL, TRACKING_CATEGORIES } from '../../constants'; import PipelineMultiActions from './pipeline_multi_actions.vue'; import PipelinesManualActions from './pipelines_manual_actions.vue'; -import PipelinesManualActionsLegacy from './pipelines_manual_actions_legacy.vue'; export default { BUTTON_TOOLTIP_RETRY, @@ -19,9 +17,8 @@ export default { GlButton, PipelineMultiActions, PipelinesManualActions, - PipelinesManualActionsLegacy, }, - mixins: [Tracking.mixin(), glFeatureFlagsMixin()], + mixins: [Tracking.mixin()], props: { pipeline: { type: Object, @@ -39,21 +36,11 @@ export default { }; }, computed: { - shouldLazyLoadActions() { - return this.glFeatures.lazyLoadPipelineDropdownActions; - }, hasActions() { return ( this.pipeline?.details?.has_manual_actions || this.pipeline?.details?.has_scheduled_actions ); }, - actions() { - if (!this.pipeline || !this.pipeline.details) { - return []; - } - const { details } = this.pipeline; - return [...(details.manual_actions || []), ...(details.scheduled_actions || [])]; - }, isCancelling() { return this.cancelingPipeline === this.pipeline.id; }, @@ -86,12 +73,7 @@ export default { <template> <div class="gl-text-right"> <div class="btn-group"> - <pipelines-manual-actions v-if="hasActions && shouldLazyLoadActions" :iid="pipeline.iid" /> - - <pipelines-manual-actions-legacy - v-if="actions.length > 0 && !shouldLazyLoadActions" - :actions="actions" - /> + <pipelines-manual-actions v-if="hasActions" :iid="pipeline.iid" /> <gl-button v-if="pipeline.flags.retryable" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions_legacy.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions_legacy.vue deleted file mode 100644 index b08eb4153ce..00000000000 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions_legacy.vue +++ /dev/null @@ -1,112 +0,0 @@ -<script> -import { GlDropdown, GlDropdownItem, GlIcon, GlTooltipDirective } from '@gitlab/ui'; -import { createAlert } from '~/alert'; -import axios from '~/lib/utils/axios_utils'; -import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; -import { s__, __, sprintf } from '~/locale'; -import Tracking from '~/tracking'; -import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; -import eventHub from '../../event_hub'; -import { TRACKING_CATEGORIES } from '../../constants'; - -export default { - name: 'PipelinesManualActionsLegacy', - directives: { - GlTooltip: GlTooltipDirective, - }, - components: { - GlCountdown, - GlDropdown, - GlDropdownItem, - GlIcon, - }, - mixins: [Tracking.mixin()], - props: { - actions: { - type: Array, - required: true, - }, - }, - data() { - return { - isLoading: false, - }; - }, - methods: { - async onClickAction(action) { - if (action.scheduled_at) { - const confirmationMessage = sprintf( - s__( - 'DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes.', - ), - { jobName: action.name }, - ); - - const confirmed = await confirmAction(confirmationMessage); - - if (!confirmed) { - return; - } - } - - this.isLoading = true; - - /** - * Ideally, the component would not make an api call directly. - * However, in order to use the eventhub and know when to - * toggle back the `isLoading` property we'd need an ID - * to track the request with a wacther - since this component - * is rendered at least 20 times in the same page, moving the - * api call directly here is the most performant solution - */ - axios - .post(`${action.path}.json`) - .then(() => { - this.isLoading = false; - eventHub.$emit('updateTable'); - }) - .catch(() => { - this.isLoading = false; - createAlert({ message: __('An error occurred while making the request.') }); - }); - }, - isActionDisabled(action) { - if (action.playable === undefined) { - return false; - } - - return !action.playable; - }, - trackClick() { - this.track('click_manual_actions', { label: TRACKING_CATEGORIES.table }); - }, - }, -}; -</script> -<template> - <gl-dropdown - v-gl-tooltip - :title="__('Run manual or delayed jobs')" - :loading="isLoading" - data-testid="pipelines-manual-actions-dropdown" - right - lazy - icon="play" - @shown="trackClick" - > - <gl-dropdown-item - v-for="action in actions" - :key="action.path" - :disabled="isActionDisabled(action)" - @click="onClickAction(action)" - > - <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap"> - {{ action.name }} - <span v-if="action.scheduled_at"> - <gl-icon name="clock" /> - <gl-countdown :end-date-string="action.scheduled_at" /> - </span> - </div> - </gl-dropdown-item> - </gl-dropdown> -</template> diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue index ca5a9b70b34..81a57e96b3c 100644 --- a/app/assets/javascripts/search/sidebar/components/app.vue +++ b/app/assets/javascripts/search/sidebar/components/app.vue @@ -1,6 +1,8 @@ <script> import { mapState, mapGetters } from 'vuex'; import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue'; +import ScopeNewNavigation from '~/search/sidebar/components/scope_new_navigation.vue'; +import SidebarPortal from '~/super_sidebar/components/sidebar_portal.vue'; import { SCOPE_ISSUES, SCOPE_MERGE_REQUESTS, SCOPE_BLOB } from '../constants'; import ResultsFilters from './results_filters.vue'; import LanguageFilter from './language_filter/index.vue'; @@ -10,10 +12,12 @@ export default { components: { ResultsFilters, ScopeNavigation, + ScopeNewNavigation, LanguageFilter, + SidebarPortal, }, computed: { - ...mapState(['urlQuery']), + ...mapState(['urlQuery', 'useNewNavigation']), ...mapGetters(['currentScope']), showIssueAndMergeFilters() { return this.currentScope === SCOPE_ISSUES || this.currentScope === SCOPE_MERGE_REQUESTS; @@ -26,7 +30,15 @@ export default { </script> <template> + <section v-if="useNewNavigation"> + <sidebar-portal> + <scope-new-navigation /> + <results-filters v-if="showIssueAndMergeFilters" /> + <language-filter v-if="showBlobFilter" /> + </sidebar-portal> + </section> <section + v-else class="search-sidebar gl-display-flex gl-flex-direction-column gl-md-mr-5 gl-mb-6 gl-mt-5" > <scope-navigation /> diff --git a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue index 1c81f652387..fc41baee831 100644 --- a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue +++ b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue @@ -3,8 +3,8 @@ import { GlNav, GlNavItem, GlIcon } from '@gitlab/ui'; import { mapActions, mapState } from 'vuex'; import { s__ } from '~/locale'; import Tracking from '~/tracking'; +import { formatSearchResultCount, addCountOverLimit } from '~/search/store/utils'; import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants'; -import { formatSearchResultCount } from '../../store/utils'; import { slugifyWithUnderscore } from '../../../lib/utils/text_utility'; export default { @@ -28,11 +28,11 @@ export default { }, methods: { ...mapActions(['fetchSidebarCount']), - showFormatedCount(count) { - return formatSearchResultCount(count); + showFormatedCount(countString) { + return formatSearchResultCount(countString); }, - isCountOverLimit(count) { - return count.includes('+'); + isCountOverLimit(countString) { + return Boolean(addCountOverLimit(countString)); }, handleClick(scope) { this.track('click_menu_item', { label: `vertical_navigation_${scope}` }); diff --git a/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue new file mode 100644 index 00000000000..86b7cc577a6 --- /dev/null +++ b/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue @@ -0,0 +1,40 @@ +<script> +import { mapActions, mapState, mapGetters } from 'vuex'; +import { s__ } from '~/locale'; +import Tracking from '~/tracking'; +import NavItem from '~/super_sidebar/components/nav_item.vue'; +import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants'; + +export default { + name: 'ScopeNewNavigation', + i18n: { + countOverLimitLabel: s__('GlobalSearch|Result count is over limit.'), + }, + components: { + NavItem, + }, + mixins: [Tracking.mixin()], + computed: { + ...mapState(['navigation', 'urlQuery']), + ...mapGetters(['navigationItems']), + }, + created() { + if (this.urlQuery?.search) { + this.fetchSidebarCount(); + } + }, + methods: { + ...mapActions(['fetchSidebarCount']), + }, + NAV_LINK_DEFAULT_CLASSES, + NAV_LINK_COUNT_DEFAULT_CLASSES, +}; +</script> + +<template> + <nav data-testid="search-filter" class="gl-py-2 gl-relative"> + <ul class="gl-px-2 gl-list-style-none"> + <nav-item v-for="item in navigationItems" :key="`menu-${item.title}`" :item="item" /> + </ul> + </nav> +</template> diff --git a/app/assets/javascripts/search/sidebar/constants/index.js b/app/assets/javascripts/search/sidebar/constants/index.js index 9cd366a87fd..9519154a571 100644 --- a/app/assets/javascripts/search/sidebar/constants/index.js +++ b/app/assets/javascripts/search/sidebar/constants/index.js @@ -12,7 +12,7 @@ export const NAV_LINK_DEFAULT_CLASSES = [ 'gl-justify-content-space-between', ]; export const NAV_LINK_COUNT_DEFAULT_CLASSES = ['gl-font-sm', 'gl-font-weight-normal']; -export const HR_DEFAULT_CLASSES = ['gl-m-5', 'gl-border-gray-100']; +export const HR_DEFAULT_CLASSES = ['gl-my-5', 'gl-mx-5', 'gl-border-gray-100']; export const ONLY_SHOW_MD = ['gl-display-none', 'gl-md-display-block']; export const TRACKING_LABEL_CHECKBOX = 'Checkbox'; diff --git a/app/assets/javascripts/super_sidebar/components/create_menu.vue b/app/assets/javascripts/super_sidebar/components/create_menu.vue index d3bb31a69fa..4cff4642cf7 100644 --- a/app/assets/javascripts/super_sidebar/components/create_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/create_menu.vue @@ -1,6 +1,10 @@ <script> import { GlDisclosureDropdown, GlTooltip } from '@gitlab/ui'; import { __ } from '~/locale'; +import { DROPDOWN_Y_OFFSET } from '../constants'; + +// Left offset required for the dropdown to be aligned with the super sidebar +const DROPDOWN_X_OFFSET = -147; export default { components: { @@ -16,7 +20,22 @@ export default { required: true, }, }, + data() { + return { + dropdownOpen: false, + }; + }, toggleId: 'create-menu-toggle', + popperOptions: { + modifiers: [ + { + name: 'offset', + options: { + offset: [DROPDOWN_X_OFFSET, DROPDOWN_Y_OFFSET], + }, + }, + ], + }, }; </script> @@ -30,9 +49,17 @@ export default { text-sr-only :toggle-text="$options.i18n.createNew" :toggle-id="$options.toggleId" + :popper-options="$options.popperOptions" data-qa-selector="new_menu_toggle" + @shown="dropdownOpen = true" + @hidden="dropdownOpen = false" /> - <gl-tooltip :target="`#${$options.toggleId}`" placement="bottom" container="#super-sidebar"> + <gl-tooltip + v-if="!dropdownOpen" + :target="`#${$options.toggleId}`" + placement="bottom" + container="#super-sidebar" + > {{ $options.i18n.createNew }} </gl-tooltip> </div> diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue index 32836ecb4e6..01b214f4b2b 100644 --- a/app/assets/javascripts/super_sidebar/components/help_center.vue +++ b/app/assets/javascripts/super_sidebar/components/help_center.vue @@ -6,7 +6,10 @@ import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility'; import { __ } from '~/locale'; import { STORAGE_KEY } from '~/whats_new/utils/notification'; import Tracking from '~/tracking'; -import { HELP_MENU_TRACKING_DEFAULTS } from '../constants'; +import { DROPDOWN_Y_OFFSET, HELP_MENU_TRACKING_DEFAULTS } from '../constants'; + +// Left offset required for the dropdown to be aligned with the super sidebar +const DROPDOWN_X_OFFSET = -4; export default { components: { @@ -185,12 +188,23 @@ export default { }); }, }, + popperOptions: { + modifiers: [ + { + name: 'offset', + options: { + offset: [DROPDOWN_X_OFFSET, DROPDOWN_Y_OFFSET], + }, + }, + ], + }, }; </script> <template> <gl-disclosure-dropdown ref="dropdown" + :popper-options="$options.popperOptions" @shown="trackDropdownToggle(true)" @hidden="trackDropdownToggle(false)" > diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue index 53698a808a7..223fbe6d078 100644 --- a/app/assets/javascripts/super_sidebar/components/nav_item.vue +++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue @@ -160,7 +160,7 @@ export default { ></div> <div class="gl-flex-shrink-0 gl-w-6 gl-mx-3"> <slot name="icon"> - <gl-icon v-if="item.icon" :name="item.icon" class="gl-ml-2" /> + <gl-icon v-if="item.icon" :name="item.icon" class="gl-ml-2 item-icon" /> <gl-icon v-else-if="draggable" name="grip" diff --git a/app/assets/javascripts/super_sidebar/components/pinned_section.vue b/app/assets/javascripts/super_sidebar/components/pinned_section.vue index 3c12e193d80..9595bdfb632 100644 --- a/app/assets/javascripts/super_sidebar/components/pinned_section.vue +++ b/app/assets/javascripts/super_sidebar/components/pinned_section.vue @@ -71,7 +71,7 @@ export default { @click.prevent="expanded = !expanded" > <div class="gl-flex-shrink-0 gl-w-6 gl-mx-3"> - <gl-icon name="thumbtack" class="gl-ml-2" /> + <gl-icon name="thumbtack" class="gl-ml-2 item-icon" /> </div> <span class="gl-font-weight-bold gl-font-sm gl-flex-grow-1">{{ $options.i18n.pinned }}</span> diff --git a/app/assets/javascripts/super_sidebar/components/user_menu.vue b/app/assets/javascripts/super_sidebar/components/user_menu.vue index d8452900e34..c90d1ad9c3e 100644 --- a/app/assets/javascripts/super_sidebar/components/user_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/user_menu.vue @@ -11,9 +11,12 @@ import { s__, __, sprintf } from '~/locale'; import NewNavToggle from '~/nav/components/new_nav_toggle.vue'; import Tracking from '~/tracking'; import PersistentUserCallout from '~/persistent_user_callout'; -import { USER_MENU_TRACKING_DEFAULTS } from '../constants'; +import { USER_MENU_TRACKING_DEFAULTS, DROPDOWN_Y_OFFSET } from '../constants'; import UserNameGroup from './user_name_group.vue'; +// Left offset required for the dropdown to be aligned with the super sidebar +const DROPDOWN_X_OFFSET = -211; + export default { feedbackUrl: 'https://gitlab.com/gitlab-org/gitlab/-/issues/403059', i18n: { @@ -216,6 +219,16 @@ export default { }); }, }, + popperOptions: { + modifiers: [ + { + name: 'offset', + options: { + offset: [DROPDOWN_X_OFFSET, DROPDOWN_Y_OFFSET], + }, + }, + ], + }, }; </script> @@ -223,7 +236,7 @@ export default { <div> <gl-disclosure-dropdown ref="userDropdown" - placement="right" + :popper-options="$options.popperOptions" data-testid="user-dropdown" data-qa-selector="user_menu" @shown="onShow" diff --git a/app/assets/javascripts/super_sidebar/constants.js b/app/assets/javascripts/super_sidebar/constants.js index 3dbd12b4ff7..5c4a6a9dfc1 100644 --- a/app/assets/javascripts/super_sidebar/constants.js +++ b/app/assets/javascripts/super_sidebar/constants.js @@ -33,3 +33,5 @@ export const HELP_MENU_TRACKING_DEFAULTS = { export const SIDEBAR_PINS_EXPANDED_COOKIE = 'sidebar_pinned_section_expanded'; export const SIDEBAR_COOKIE_EXPIRATION = 365 * 10; + +export const DROPDOWN_Y_OFFSET = 4; 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 c19dfe663f4..25cf5335fb5 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 @@ -5,7 +5,7 @@ import { STATUS_MERGED } from '~/issues/constants'; import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import { HTTP_STATUS_UNAUTHORIZED } from '~/lib/utils/http_status'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { s__, __ } from '~/locale'; +import { s__, __, sprintf } from '~/locale'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import eventHub from '../../event_hub'; import approvalsMixin from '../../mixins/approvals'; @@ -84,13 +84,22 @@ export default { return Boolean(this.action); }, invalidRules() { - return this.approvals.approvalState?.invalidApproversRules || []; + return this.approvals.approvalState?.rules?.filter((rule) => rule.invalid) || []; + }, + invalidApprovedRules() { + return this.invalidRules.filter((rule) => rule.allowMergeWhenInvalid); + }, + invalidFailedRules() { + return this.invalidRules.filter((rule) => !rule.allowMergeWhenInvalid); }, hasInvalidRules() { return this.mr.mergeRequestApproversAvailable && this.invalidRules.length; }, - invalidRulesText() { - return this.invalidRules.length; + hasInvalidApprovedRules() { + return this.mr.mergeRequestApproversAvailable && this.invalidApprovedRules.length; + }, + hasInvalidFailedRules() { + return this.mr.mergeRequestApproversAvailable && this.invalidFailedRules.length; }, approvedBy() { return this.approvals.approvedBy?.nodes || []; @@ -133,11 +142,29 @@ export default { return null; }, - pluralizedRuleText() { - return this.invalidRules.length > 1 + pluralizedApprovedRuleText() { + return this.invalidApprovedRules.length > 1 ? this.$options.i18n.invalidRulesPlural : this.$options.i18n.invalidRuleSingular; }, + pluralizedFailedRuleText() { + return this.invalidFailedRules.length > 1 + ? this.$options.i18n.invalidFailedRulesPlural + : this.$options.i18n.invalidFailedRuleSingular; + }, + pluralizedRuleText() { + return [ + this.hasInvalidFailedRules + ? sprintf(this.pluralizedFailedRuleText, { rules: this.invalidFailedRules.length }) + : null, + this.hasInvalidApprovedRules + ? sprintf(this.pluralizedApprovedRuleText, { rules: this.invalidApprovedRules.length }) + : null, + ] + .filter((text) => Boolean(text)) + .join(', ') + .concat('.'); + }, }, methods: { approve() { @@ -205,11 +232,13 @@ export default { FETCH_LOADING, linkToInvalidRules: INVALID_RULES_DOCS_PATH, i18n: { - invalidRuleSingular: s__( - 'mrWidget|%{rules} invalid rule has been approved automatically, as no one can approve it.', + invalidRuleSingular: s__('mrWidget|%{rules} invalid rule has been approved automatically'), + invalidRulesPlural: s__('mrWidget|%{rules} invalid rules have been approved automatically'), + invalidFailedRuleSingular: s__( + "mrWidget|%{dangerStart}%{rules} rule can't be approved%{dangerEnd}", ), - invalidRulesPlural: s__( - 'mrWidget|%{rules} invalid rules have been approved automatically, as no one can approve them.', + invalidFailedRulesPlural: s__( + "mrWidget|%{dangerStart}%{rules} rules can't be approved%{dangerEnd}", ), learnMore: __('Learn more.'), }, @@ -255,7 +284,9 @@ export default { </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 #danger="{ content }"> + <span class="gl-font-weight-bold text-danger">{{ content }}</span> + </template> </gl-sprintf> </div> </div> diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 4e827d71126..3486f231b39 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -122,12 +122,7 @@ export default { return [`<details><summary>${expandText}</summary>`, `{text}`, '</details>'].join('\n'); }, showAiActions() { - return ( - this.resourceGlobalId && - this.glFeatures.openaiExperimentation && - this.glFeatures.summarizeNotes && - this.glFeatures.summarizeComments - ); + return this.resourceGlobalId && this.glFeatures.summarizeComments; }, }, watch: { |