diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-22 00:14:46 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-22 00:14:46 +0300 |
commit | a3e6d34643e760d1a8b8bd1e7e32d8d74c1ea678 (patch) | |
tree | 1228f600e98bfe626c313ffa61a60a4b7d162426 /app | |
parent | d5ff0674315196e88f48dc0838486b44cd005628 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
21 files changed, 246 insertions, 180 deletions
diff --git a/app/assets/javascripts/admin/abuse_report/components/report_actions.vue b/app/assets/javascripts/admin/abuse_report/components/report_actions.vue index e005e183c9f..3e9cc36b8b2 100644 --- a/app/assets/javascripts/admin/abuse_report/components/report_actions.vue +++ b/app/assets/javascripts/admin/abuse_report/components/report_actions.vue @@ -64,7 +64,6 @@ export default { }, computed: { getDrawerHeaderHeight() { - if (!this.showActionsDrawer || gon.use_new_navigation) return '0'; return getContentWrapperHeight(); }, isFormValid() { diff --git a/app/assets/javascripts/batch_comments/mixins/resolved_status.js b/app/assets/javascripts/batch_comments/mixins/resolved_status.js index fddb843bb52..da7c7809bed 100644 --- a/app/assets/javascripts/batch_comments/mixins/resolved_status.js +++ b/app/assets/javascripts/batch_comments/mixins/resolved_status.js @@ -25,11 +25,10 @@ export default { resolvedStatusMessage() { let message; const discussionResolved = this.isDiscussionResolved( - this.draft ? this.draft.discussion_id : this.discussionId, + 'draft' in this ? this.draft.discussion_id : this.discussionId, ); - const discussionToBeResolved = this.draft - ? this.draft.resolve_discussion - : this.resolveDiscussion; + const discussionToBeResolved = + 'draft' in this ? this.draft.resolve_discussion : this.resolveDiscussion; if (discussionToBeResolved && discussionResolved && !this.$options.showStaysResolved) { return undefined; diff --git a/app/assets/javascripts/behaviors/shortcuts/keybindings.js b/app/assets/javascripts/behaviors/shortcuts/keybindings.js index 941662635ea..15229689306 100644 --- a/app/assets/javascripts/behaviors/shortcuts/keybindings.js +++ b/app/assets/javascripts/behaviors/shortcuts/keybindings.js @@ -538,13 +538,10 @@ const GLOBAL_SHORTCUTS_GROUP = { GO_TO_YOUR_TODO_LIST, TOGGLE_PERFORMANCE_BAR, HIDE_APPEARING_CONTENT, + TOGGLE_SUPER_SIDEBAR, ], }; -if (gon.use_new_navigation) { - GLOBAL_SHORTCUTS_GROUP.keybindings.push(TOGGLE_SUPER_SIDEBAR); -} - export const EDITING_SHORTCUTS_GROUP = { id: 'editing', name: __('Editing'), diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js index 9514ad853b0..1d6819d4b04 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js @@ -198,11 +198,7 @@ export default class Shortcuts { } static focusSearch(e) { - if (gon.use_new_navigation) { - document.querySelector('#super-sidebar-search')?.click(); - } else { - document.querySelector('#search')?.focus(); - } + document.querySelector('#super-sidebar-search')?.click(); if (e.preventDefault) { e.preventDefault(); diff --git a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue index 257c482cf1d..b952e0059bb 100644 --- a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue +++ b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue @@ -67,7 +67,7 @@ export default { return featureFlag.iid ? `^${featureFlag.iid}` : ''; }, canDeleteFlag(flag) { - return !this.permissions || (flag.scopes || []).every((scope) => scope.can_update); + return (flag.scopes || []).every((scope) => scope.can_update); }, setDeleteModalData(featureFlag) { this.deleteFeatureFlagUrl = featureFlag.destroy_path; diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js index 7a729cc58c3..8f3681952c6 100644 --- a/app/assets/javascripts/header.js +++ b/app/assets/javascripts/header.js @@ -1,10 +1,8 @@ // TODO: Remove this with the removal of the old navigation. // See https://gitlab.com/groups/gitlab-org/-/epics/11875. -import Vue from 'vue'; import { highCountTrim } from '~/lib/utils/text_utility'; import Tracking from '~/tracking'; -import Translate from '~/vue_shared/translate'; /** * Updates todo counter when todos are toggled. @@ -29,76 +27,6 @@ export default function initTodoToggle() { }); } -export function initStatusTriggers() { - const setStatusModalTriggerEl = document.querySelector('.js-set-status-modal-trigger'); - - if (setStatusModalTriggerEl) { - setStatusModalTriggerEl.addEventListener('click', () => { - const topNavbar = document.querySelector('.navbar-gitlab'); - const buttonWithinTopNav = topNavbar && topNavbar.contains(setStatusModalTriggerEl); - Tracking.event(undefined, 'click_button', { - label: 'user_edit_status', - property: buttonWithinTopNav ? 'navigation_top' : 'nav_user_menu', - }); - - import( - /* webpackChunkName: 'statusModalBundle' */ './set_status_modal/set_status_modal_wrapper.vue' - ) - .then(({ default: SetStatusModalWrapper }) => { - const setStatusModalWrapperEl = document.querySelector('.js-set-status-modal-wrapper'); - const statusModalElement = document.createElement('div'); - setStatusModalWrapperEl.appendChild(statusModalElement); - - Vue.use(Translate); - - // eslint-disable-next-line no-new - new Vue({ - el: statusModalElement, - data() { - const { - currentEmoji, - defaultEmoji, - currentMessage, - currentAvailability, - currentClearStatusAfter, - } = setStatusModalWrapperEl.dataset; - - return { - currentEmoji, - defaultEmoji, - currentMessage, - currentAvailability, - currentClearStatusAfter, - }; - }, - render(createElement) { - const { - currentEmoji, - defaultEmoji, - currentMessage, - currentAvailability, - currentClearStatusAfter, - } = this; - - return createElement(SetStatusModalWrapper, { - props: { - currentEmoji, - defaultEmoji, - currentMessage, - currentAvailability, - currentClearStatusAfter, - }, - }); - }, - }); - }) - .catch(() => {}); - }); - - setStatusModalTriggerEl.classList.add('ready'); - } -} - function trackShowUserDropdownLink(trackEvent, elToTrack, el) { const { trackLabel, trackProperty } = elToTrack.dataset; @@ -119,7 +47,4 @@ export function initNavUserDropdownTracking() { } } -if (!gon?.use_new_navigation) { - requestIdleCallback(initStatusTriggers); -} requestIdleCallback(initNavUserDropdownTracking); diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue index eaa99556994..729f248ca2b 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue @@ -187,6 +187,11 @@ export default { return typeof this.drawioUrl === 'string' && this.drawioUrl.length > 0; }, }, + watch: { + title() { + this.updateCommitMessage(); + }, + }, mounted() { if (!this.commitMessage) this.updateCommitMessage(); @@ -321,7 +326,6 @@ export default { :required="true" :autofocus="!pageInfo.persisted" :placeholder="$options.i18n.title.placeholder" - @input="updateCommitMessage" /> </gl-form-group> </div> @@ -361,8 +365,8 @@ export default { :drawio-enabled="drawioEnabled" @contentEditor="notifyContentEditorActive" @markdownField="notifyContentEditorInactive" - @keydown.ctrl.enter="submitFormShortcut" - @keydown.meta.enter="submitFormShortcut" + @keydown.ctrl.enter="submitFormWithShortcut" + @keydown.meta.enter="submitFormWithShortcut" /> <div class="form-text gl-text-gray-600"> <gl-sprintf diff --git a/app/assets/javascripts/set_status_modal/constants.js b/app/assets/javascripts/set_status_modal/constants.js index 53e64db1497..86e24c29775 100644 --- a/app/assets/javascripts/set_status_modal/constants.js +++ b/app/assets/javascripts/set_status_modal/constants.js @@ -12,3 +12,5 @@ export const AVAILABILITY_STATUS = { BUSY: 'busy', NOT_SET: 'not_set', }; + +export const SET_STATUS_MODAL_ID = 'set-user-status-modal'; diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue index 270d7f0d182..7f229e5c5ed 100644 --- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue +++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue @@ -2,17 +2,18 @@ import { GlToast, GlTooltipDirective, GlModal } from '@gitlab/ui'; import Vue from 'vue'; import { createAlert } from '~/alert'; -import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants'; +import { BV_HIDE_MODAL } from '~/lib/utils/constants'; import { s__ } from '~/locale'; import { updateUserStatus } from '~/rest_api'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { isUserBusy, computedClearStatusAfterValue } from './utils'; -import { AVAILABILITY_STATUS } from './constants'; +import { AVAILABILITY_STATUS, SET_STATUS_MODAL_ID } from './constants'; import SetStatusForm from './set_status_form.vue'; Vue.use(GlToast); export default { + SET_STATUS_MODAL_ID, components: { GlModal, SetStatusForm, @@ -29,11 +30,13 @@ export default { }, currentEmoji: { type: String, - required: true, + required: false, + default: '', }, currentMessage: { type: String, - required: true, + required: false, + default: '', }, currentAvailability: { type: String, @@ -51,7 +54,6 @@ export default { defaultEmojiTag: '', emoji: this.currentEmoji, message: this.currentMessage, - modalId: 'set-user-status-modal', availability: isUserBusy(this.currentAvailability), clearStatusAfter: null, }; @@ -65,11 +67,11 @@ export default { }, }, mounted() { - this.$root.$emit(BV_SHOW_MODAL, this.modalId); + this.$emit('mounted'); }, methods: { closeModal() { - this.$root.$emit(BV_HIDE_MODAL, this.modalId); + this.$root.$emit(BV_HIDE_MODAL, SET_STATUS_MODAL_ID); }, removeStatus() { this.availability = false; @@ -132,7 +134,7 @@ export default { <template> <gl-modal :title="s__('SetStatusModal|Set a status')" - :modal-id="modalId" + :modal-id="$options.SET_STATUS_MODAL_ID" :action-primary="$options.actionPrimary" :action-secondary="$options.actionSecondary" modal-class="set-user-status-modal" diff --git a/app/assets/javascripts/super_sidebar/components/scroll_scrim.vue b/app/assets/javascripts/super_sidebar/components/scroll_scrim.vue new file mode 100644 index 00000000000..0e849b08a39 --- /dev/null +++ b/app/assets/javascripts/super_sidebar/components/scroll_scrim.vue @@ -0,0 +1,72 @@ +<script> +export default { + name: 'ScrollScrim', + data() { + return { + topBoundaryVisible: true, + bottomBoundaryVisible: true, + }; + }, + computed: { + scrimClasses() { + return { + 'top-scrim-visible': !this.topBoundaryVisible, + 'bottom-scrim-visible gl-border-b': !this.bottomBoundaryVisible, + }; + }, + }, + mounted() { + this.observeScroll(); + }, + beforeDestroy() { + this.scrollObserver?.disconnect(); + }, + + methods: { + observeScroll() { + const root = this.$el; + + const options = { + rootMargin: '8px', + root, + threshold: 1.0, + }; + + this.scrollObserver?.disconnect(); + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + this[entry.target?.$__visibilityProp] = entry.isIntersecting; + }); + }, options); + + const topBoundary = this.$refs['top-boundary']; + const bottomBoundary = this.$refs['bottom-boundary']; + + topBoundary.$__visibilityProp = 'topBoundaryVisible'; + observer.observe(topBoundary); + + bottomBoundary.$__visibilityProp = 'bottomBoundaryVisible'; + observer.observe(bottomBoundary); + + this.scrollObserver = observer; + }, + }, +}; +</script> + +<template> + <div class="gl-scroll-scrim gl-overflow-auto" :class="scrimClasses"> + <div class="top-scrim-wrapper"> + <div class="top-scrim"></div> + </div> + <div ref="top-boundary"></div> + + <slot></slot> + + <div ref="bottom-boundary"></div> + <div class="bottom-scrim-wrapper"> + <div class="bottom-scrim"></div> + </div> + </div> +</template> diff --git a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue index c04addf5262..5f067621814 100644 --- a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue @@ -194,7 +194,7 @@ export default { /> <ul aria-labelledby="super-sidebar-context-header" - class="gl-p-0 gl-list-style-none" + class="gl-p-0 gl-mb-0 gl-list-style-none" data-testid="non-static-items-section" > <template v-for="item in nonStaticItems"> diff --git a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue index 8f95dd1dcec..ebdc0026539 100644 --- a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue +++ b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue @@ -20,6 +20,7 @@ import HelpCenter from './help_center.vue'; import SidebarMenu from './sidebar_menu.vue'; import SidebarPeekBehavior from './sidebar_peek_behavior.vue'; import SidebarHoverPeekBehavior from './sidebar_hover_peek_behavior.vue'; +import ScrollScrim from './scroll_scrim.vue'; export default { components: { @@ -30,6 +31,7 @@ export default { SidebarPeekBehavior, SidebarHoverPeekBehavior, SidebarPortalTarget, + ScrollScrim, TrialStatusWidget: () => import('ee_component/contextual_sidebar/components/trial_status_widget.vue'), TrialStatusPopover: () => @@ -202,7 +204,7 @@ export default { <div class="contextual-nav gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-overflow-hidden" > - <div class="gl-flex-grow-1 gl-overflow-auto" data-testid="nav-container"> + <scroll-scrim class="gl-flex-grow-1" data-testid="nav-container"> <div id="super-sidebar-context-header" class="gl-px-5 gl-pt-3 gl-pb-2 gl-m-0 gl-reset-line-height gl-font-weight-bold gl-font-sm super-sidebar-context-header" @@ -218,8 +220,8 @@ export default { :update-pins-url="sidebarData.update_pins_url" /> <sidebar-portal-target /> - </div> - <div class="gl-p-3"> + </scroll-scrim> + <div class="gl-p-2"> <help-center ref="helpCenter" :sidebar-data="sidebarData" /> <gl-button v-if="sidebarData.is_admin" diff --git a/app/assets/javascripts/super_sidebar/components/user_menu.vue b/app/assets/javascripts/super_sidebar/components/user_menu.vue index ef4ba8d9056..2823aeaee7b 100644 --- a/app/assets/javascripts/super_sidebar/components/user_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/user_menu.vue @@ -5,11 +5,13 @@ import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem, GlButton, + GlModalDirective, } from '@gitlab/ui'; import SafeHtml from '~/vue_shared/directives/safe_html'; import { s__, __, sprintf } from '~/locale'; import Tracking from '~/tracking'; import PersistentUserCallout from '~/persistent_user_callout'; +import { SET_STATUS_MODAL_ID } from '~/set_status_modal/constants'; import { USER_MENU_TRACKING_DEFAULTS, DROPDOWN_Y_OFFSET, IMPERSONATING_OFFSET } from '../constants'; import UserMenuProfileItem from './user_menu_profile_item.vue'; @@ -18,6 +20,7 @@ const DROPDOWN_X_OFFSET_BASE = -211; const DROPDOWN_X_OFFSET_IMPERSONATING = DROPDOWN_X_OFFSET_BASE + IMPERSONATING_OFFSET; export default { + SET_STATUS_MODAL_ID, i18n: { setStatus: s__('SetStatusModal|Set status'), editStatus: s__('SetStatusModal|Edit status'), @@ -36,9 +39,14 @@ export default { GlDisclosureDropdownItem, GlButton, UserMenuProfileItem, + SetStatusModal: () => + import( + /* webpackChunkName: 'statusModalBundle' */ '~/set_status_modal/set_status_modal_wrapper.vue' + ), }, directives: { SafeHtml, + GlModal: GlModalDirective, }, mixins: [Tracking.mixin()], inject: ['isImpersonating'], @@ -48,6 +56,11 @@ export default { type: Object, }, }, + data() { + return { + setStatusModalReady: false, + }; + }, computed: { toggleText() { return sprintf(__('%{user} user’s menu'), { user: this.data.name }); @@ -61,7 +74,8 @@ export default { return { text: statusLabel, extraAttrs: { - class: 'js-set-status-modal-trigger', + ...USER_MENU_TRACKING_DEFAULTS, + 'data-track-label': 'user_edit_status', }, }; }, @@ -140,24 +154,22 @@ export default { }; }, statusModalData() { - const defaultData = { - 'data-current-emoji': '', - 'data-current-message': '', - 'data-default-emoji': 'speech_balloon', - }; + if (!this.data?.status?.can_update) { + return null; + } const { busy, customized } = this.data.status; if (!busy && !customized) { - return defaultData; + return {}; } + const { emoji, message, availability, clear_after: clearAfter } = this.data.status; return { - ...defaultData, - 'data-current-emoji': this.data.status.emoji, - 'data-current-message': this.data.status.message, - 'data-current-availability': this.data.status.availability, - 'data-current-clear-status-after': this.data.status.clear_after, + 'current-emoji': emoji || '', + 'current-message': message || '', + 'current-availability': availability || '', + 'current-clear-status-after': clearAfter || '', }; }, buyPipelineMinutesCalloutData() { @@ -248,7 +260,8 @@ export default { <gl-disclosure-dropdown-group bordered> <gl-disclosure-dropdown-item - v-if="data.status.can_update" + v-if="setStatusModalReady && statusModalData" + v-gl-modal="$options.SET_STATUS_MODAL_ID" :item="statusItem" data-testid="status-item" @action="closeDropdown" @@ -304,11 +317,11 @@ export default { @action="trackSignOut" /> </gl-disclosure-dropdown> - - <div - v-if="data.status.can_update" - class="js-set-status-modal-wrapper" + <set-status-modal + v-if="statusModalData" + default-emoji="speech_balloon" v-bind="statusModalData" - ></div> + @mounted="setStatusModalReady = true" + /> </div> </template> diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js index b4ad9a20b76..6aa974878d0 100644 --- a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js +++ b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js @@ -3,7 +3,6 @@ import { GlToast } from '@gitlab/ui'; import VueApollo from 'vue-apollo'; import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils'; import createDefaultClient from '~/lib/graphql'; -import { initStatusTriggers } from '../header'; import { JS_TOGGLE_EXPAND_CLASS } from './constants'; import createStore from './components/global_search/store'; import { @@ -153,5 +152,3 @@ export const initSuperSidebarToggle = () => { }, }); }; - -requestIdleCallback(initStatusTriggers); diff --git a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue index 3412848a9b7..a5c34b4b619 100644 --- a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue +++ b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue @@ -86,11 +86,7 @@ export default { }, showSuperSidebarToggle() { - return gon.use_new_navigation && sidebarState.isCollapsed; - }, - - topBarClasses() { - return gon.use_new_navigation ? 'top-bar-fixed container-fluid' : ''; + return sidebarState.isCollapsed; }, }, @@ -124,7 +120,7 @@ export default { <template> <div> - <div :class="topBarClasses" data-testid="top-bar"> + <div class="top-bar-fixed container-fluid" data-testid="top-bar"> <div class="top-bar-container gl-display-flex gl-align-items-center gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid" > diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 4d4144fe9dd..9118c2a3a50 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -26,7 +26,6 @@ @import 'framework/highlight'; @import 'framework/lists'; @import 'framework/logo'; -@import 'framework/job_log'; @import 'framework/markdown_area'; @import 'framework/media_object'; @import 'framework/modal'; diff --git a/app/assets/stylesheets/framework/job_log.scss b/app/assets/stylesheets/framework/job_log.scss deleted file mode 100644 index e409facd081..00000000000 --- a/app/assets/stylesheets/framework/job_log.scss +++ /dev/null @@ -1,47 +0,0 @@ -.job-log { - font-family: $monospace-font; - padding: $gl-padding-8 $input-horizontal-padding; - margin: 0 0 $gl-padding-8; - font-size: 13px; - word-break: break-all; - word-wrap: break-word; - color: color-yiq($builds-log-bg); - border-radius: 0 0 $border-radius-default $border-radius-default; - min-height: 42px; - background-color: $builds-log-bg; -} - -.log-line { - padding: 1px $gl-padding-8 1px $job-log-line-padding; - min-height: $gl-line-height-20; -} - -.line-number { - color: $gray-500; - padding: 0 $gl-padding-8; - min-width: $job-line-number-width; - margin-left: -$job-line-number-margin; - padding-right: 1em; - user-select: none; - - &:hover, - &:active, - &:visited { - text-decoration: underline; - color: $gray-500; - } -} - -.collapsible-line { - &:hover { - background-color: rgba($white, 0.2); - } - - .arrow { - margin-left: -$job-arrow-margin; - } -} - -.loader-animation { - @include build-loader-animation; -} diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss index add5758090f..c8bf2877b5a 100644 --- a/app/assets/stylesheets/framework/super_sidebar.scss +++ b/app/assets/stylesheets/framework/super_sidebar.scss @@ -404,3 +404,57 @@ $super-sidebar-transition-hint-duration: $super-sidebar-transition-duration / 4; } } } + + +// Styles for the ScrollScrim component. +// Should eventually be moved to gitlab-ui. +// See https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1869 + +$scroll-scrim-height: 2.25rem; + +.gl-scroll-scrim { + .top-scrim-wrapper, + .bottom-scrim-wrapper { + height: $scroll-scrim-height; + opacity: 0; + position: sticky; + z-index: 1; + display: block; + left: 0; + right: 0; + pointer-events: none; + transition: opacity 0.1s; + } + + .top-scrim-wrapper { + top: 0; + margin-bottom: -$scroll-scrim-height; + + .top-scrim { + background: linear-gradient(180deg, var(--sidebar-background, $gray-10) 0%, $transparent-rgba 100%); + } + } + + .bottom-scrim-wrapper { + bottom: 0; + margin-top: -$scroll-scrim-height; + + .bottom-scrim { + background: linear-gradient(180deg, $transparent-rgba 0%, var(--sidebar-background, $gray-10)); + } + } + + .top-scrim, + .bottom-scrim { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + &.top-scrim-visible .top-scrim-wrapper, + &.bottom-scrim-visible .bottom-scrim-wrapper { + opacity: 1; + } +} diff --git a/app/assets/stylesheets/page_bundles/build.scss b/app/assets/stylesheets/page_bundles/build.scss index 6165ee6e8b4..9b96c224d88 100644 --- a/app/assets/stylesheets/page_bundles/build.scss +++ b/app/assets/stylesheets/page_bundles/build.scss @@ -166,3 +166,51 @@ margin-bottom: 0; } } + +.job-log { + font-family: $monospace-font; + padding: $gl-padding-8 $input-horizontal-padding; + margin: 0 0 $gl-padding-8; + font-size: 13px; + word-break: break-all; + word-wrap: break-word; + color: color-yiq($builds-log-bg); + border-radius: 0 0 $border-radius-default $border-radius-default; + min-height: 42px; + background-color: $builds-log-bg; +} + +.log-line { + padding: 1px $gl-padding-8 1px $job-log-line-padding; + min-height: $gl-line-height-20; +} + +.line-number { + color: $gray-500; + padding: 0 $gl-padding-8; + min-width: $job-line-number-width; + margin-left: -$job-line-number-margin; + padding-right: 1em; + user-select: none; + + &:hover, + &:active, + &:visited { + text-decoration: underline; + color: $gray-500; + } +} + +.collapsible-line { + &:hover { + background-color: rgba($white, 0.2); + } + + .arrow { + margin-left: -$job-arrow-margin; + } +} + +.loader-animation { + @include build-loader-animation; +} diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c8fc10e6c94..4c4a2dd7875 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1722,6 +1722,7 @@ class MergeRequest < ApplicationRecord variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME', value: target_branch.to_s) variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_PROTECTED', value: ProtectedBranch.protected?(target_project, target_branch).to_s) variables.append(key: 'CI_MERGE_REQUEST_TITLE', value: title) + variables.append(key: 'CI_MERGE_REQUEST_DESCRIPTION', value: description) variables.append(key: 'CI_MERGE_REQUEST_ASSIGNEES', value: assignee_username_list) if assignees.present? variables.append(key: 'CI_MERGE_REQUEST_MILESTONE', value: milestone.title) if milestone variables.append(key: 'CI_MERGE_REQUEST_LABELS', value: label_names.join(',')) if labels.present? diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb index fde56d8429e..e8a684e5da4 100644 --- a/app/services/projects/import_service.rb +++ b/app/services/projects/import_service.rb @@ -171,6 +171,8 @@ module Projects project.import_url, schemes: Project::VALID_IMPORT_PROTOCOLS, ports: Project::VALID_IMPORT_PORTS, + allow_localhost: allow_local_requests?, + allow_local_network: allow_local_requests?, dns_rebind_protection: dns_rebind_protection?) .then do |(import_url, resolved_host)| next '' if resolved_host.nil? || !import_url.scheme.in?(%w[http https]) @@ -179,6 +181,11 @@ module Projects end end + def allow_local_requests? + Rails.env.development? && # There is no known usecase for this in non-development environments + Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services? + end + def dns_rebind_protection? return false if Gitlab.http_proxy_env? |