diff options
Diffstat (limited to 'app/assets')
102 files changed, 1426 insertions, 609 deletions
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index aeb88715c11..3826ecd1ac1 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -8,6 +8,7 @@ import { updateTooltipTitle } from './lib/utils/common_utils'; import { isInVueNoteablePage } from './lib/utils/dom_utils'; import flash from './flash'; import axios from './lib/utils/axios_utils'; +import bp from './breakpoints'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd'; @@ -264,7 +265,10 @@ export class AwardsHandler { const css = { top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`, }; - if (position === 'right') { + // for xs screen we position the element on center + if (bp.getBreakpointSize() === 'xs') { + css.left = '5%'; + } else if (position === 'right') { css.left = `${$addBtn.offset().left - $menu.outerWidth() + 20}px`; $menu.addClass('is-aligned-right'); } else { diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js index 58cf057b2c2..318b7f77c7b 100644 --- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js @@ -10,10 +10,10 @@ export class CopyAsGFM { const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent); if (isIOS) return; - $(document).on('copy', '.md, .wiki', e => { + $(document).on('copy', '.md', e => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); }); - $(document).on('copy', 'pre.code.highlight, .diff-content .line_content', e => { + $(document).on('copy', 'pre.code.highlight, table.code td.line_content', e => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); }); $(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM); @@ -99,7 +99,7 @@ export class CopyAsGFM { } static transformGFMSelection(documentFragment) { - const gfmElements = documentFragment.querySelectorAll('.md, .wiki'); + const gfmElements = documentFragment.querySelectorAll('.md'); switch (gfmElements.length) { case 0: { return documentFragment; diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js index fc9286d15e6..bfb073fdcdc 100644 --- a/app/assets/javascripts/behaviors/markdown/render_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js @@ -4,6 +4,7 @@ import renderMath from './render_math'; import renderMermaid from './render_mermaid'; import highlightCurrentUser from './highlight_current_user'; import initUserPopovers from '../../user_popovers'; +import initMRPopovers from '../../mr_popover'; // Render GitLab flavoured Markdown // @@ -15,6 +16,7 @@ $.fn.renderGFM = function renderGFM() { renderMermaid(this.find('.js-render-mermaid')); highlightCurrentUser(this.find('.gfm-project_member').get()); initUserPopovers(this.find('.gfm-project_member').get()); + initMRPopovers(this.find('.gfm-merge_request').get()); return this; }; diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js index 680f2031409..670f66b005e 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js @@ -37,7 +37,7 @@ export default class ShortcutsIssuable extends Shortcuts { } // Sanity check: Make sure the selected text comes from a discussion : it can either contain a message... - let foundMessage = !!documentFragment.querySelector('.md, .wiki'); + let foundMessage = !!documentFragment.querySelector('.md'); // ... Or come from a message if (!foundMessage) { @@ -46,7 +46,7 @@ export default class ShortcutsIssuable extends Shortcuts { let node = e; do { // Text nodes don't define the `matches` method - if (node.matches && node.matches('.md, .wiki')) { + if (node.matches && node.matches('.md')) { foundMessage = true; } node = node.parentNode; diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js index 388f674f643..c95d7608e37 100644 --- a/app/assets/javascripts/clusters/clusters_bundle.js +++ b/app/assets/javascripts/clusters/clusters_bundle.js @@ -12,6 +12,8 @@ import { REQUEST_FAILURE, UPGRADE_REQUESTED, UPGRADE_REQUEST_FAILURE, + INGRESS, + INGRESS_DOMAIN_SUFFIX, } from './constants'; import ClustersService from './services/clusters_service'; import ClustersStore from './stores/clusters_store'; @@ -76,6 +78,10 @@ export default class Clusters { this.successApplicationContainer = document.querySelector('.js-cluster-application-notice'); this.showTokenButton = document.querySelector('.js-show-cluster-token'); this.tokenField = document.querySelector('.js-cluster-token'); + this.ingressDomainHelpText = document.querySelector('.js-ingress-domain-help-text'); + this.ingressDomainSnippet = this.ingressDomainHelpText.querySelector( + '.js-ingress-domain-snippet', + ); Clusters.initDismissableCallout(); initSettingsPanels(); @@ -182,6 +188,10 @@ export default class Clusters { this.checkForNewInstalls(prevApplicationMap, this.store.state.applications); this.updateContainer(prevStatus, this.store.state.status, this.store.state.statusReason); + this.toggleIngressDomainHelpText( + prevApplicationMap[INGRESS], + this.store.state.applications[INGRESS], + ); } showToken() { @@ -277,6 +287,16 @@ export default class Clusters { this.store.updateAppProperty(appId, 'requestStatus', null); } + toggleIngressDomainHelpText(ingressPreviousState, ingressNewState) { + const helpTextHidden = ingressNewState.status !== APPLICATION_STATUS.INSTALLED; + const domainSnippetText = `${ingressNewState.externalIp}${INGRESS_DOMAIN_SUFFIX}`; + + if (ingressPreviousState.status !== ingressNewState.status) { + this.ingressDomainHelpText.classList.toggle('hide', helpTextHidden); + this.ingressDomainSnippet.textContent = domainSnippetText; + } + } + saveKnativeDomain(data) { const appId = data.id; this.store.updateAppProperty(appId, 'status', APPLICATION_STATUS.UPDATING); diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue index 5b206b82fe0..d54f9ce552c 100644 --- a/app/assets/javascripts/clusters/components/applications.vue +++ b/app/assets/javascripts/clusters/components/applications.vue @@ -393,7 +393,6 @@ export default { <div slot="description" v-html="prometheusDescription"></div> </application-row> <application-row - v-if="isProjectCluster" id="runner" :logo-url="gitlabLogo" :title="applications.runner.title" @@ -409,9 +408,9 @@ export default { > <div slot="description"> {{ - s__(`ClusterIntegration|GitLab Runner connects to this - project's repository and executes CI/CD jobs, - pushing results back and deploying, + s__(`ClusterIntegration|GitLab Runner connects to the + repository and executes CI/CD jobs, + pushing results back and deploying applications to production.`) }} </div> diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js index 39022879d91..67f481f2afb 100644 --- a/app/assets/javascripts/clusters/constants.js +++ b/app/assets/javascripts/clusters/constants.js @@ -28,3 +28,4 @@ export const JUPYTER = 'jupyter'; export const KNATIVE = 'knative'; export const RUNNER = 'runner'; export const CERT_MANAGER = 'cert_manager'; +export const INGRESS_DOMAIN_SUFFIX = '.nip.io'; diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js index fba30aea9ae..e5e1cbb1e62 100644 --- a/app/assets/javascripts/commons/bootstrap.js +++ b/app/assets/javascripts/commons/bootstrap.js @@ -16,3 +16,63 @@ $.fn.extend({ .removeClass('disabled'); }, }); + +/* + Starting with bootstrap 4.3.1, bootstrap sanitizes html used for tooltips / popovers. + This extends the default whitelists with more elements / attributes: + https://getbootstrap.com/docs/4.3/getting-started/javascript/#sanitizer + */ +const whitelist = $.fn.tooltip.Constructor.Default.whiteList; + +const inputAttributes = ['value', 'type']; + +const dataAttributes = [ + 'data-toggle', + 'data-placement', + 'data-container', + 'data-title', + 'data-class', + 'data-clipboard-text', + 'data-placement', +]; + +// Whitelisting data attributes +whitelist['*'] = [ + ...whitelist['*'], + ...dataAttributes, + 'title', + 'width height', + 'abbr', + 'datetime', + 'name', + 'width', + 'height', +]; + +// Whitelist missing elements: +whitelist.label = ['for']; +whitelist.button = [...inputAttributes]; +whitelist.input = [...inputAttributes]; + +whitelist.tt = []; +whitelist.samp = []; +whitelist.kbd = []; +whitelist.var = []; +whitelist.dfn = []; +whitelist.cite = []; +whitelist.big = []; +whitelist.address = []; +whitelist.dl = []; +whitelist.dt = []; +whitelist.dd = []; +whitelist.abbr = []; +whitelist.acronym = []; +whitelist.blockquote = []; +whitelist.del = []; +whitelist.ins = []; +whitelist['gl-emoji'] = []; + +// Whitelisting SVG tags and attributes +whitelist.svg = ['viewBox']; +whitelist.use = ['xlink:href']; +whitelist.path = ['d']; diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 1fc2b7fe859..e8f8c09152a 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -19,6 +19,7 @@ import { MIN_TREE_WIDTH, MAX_TREE_WIDTH, TREE_HIDE_STATS_WIDTH, + MR_TREE_SHOW_KEY, } from '../constants'; export default { @@ -162,10 +163,13 @@ export default { 'setHighlightedRow', 'cacheTreeListWidth', 'scrollToFile', + 'toggleShowTreeList', ]), fetchData() { this.fetchDiffFiles() .then(() => { + this.hideTreeListIfJustOneFile(); + requestIdleCallback( () => { this.setDiscussions(); @@ -231,6 +235,13 @@ export default { this.scrollToFile(this.diffFiles[targetIndex].file_path); } }, + hideTreeListIfJustOneFile() { + const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY); + + if ((storedTreeShow === null && this.diffFiles.length <= 1) || storedTreeShow === 'false') { + this.toggleShowTreeList(false); + } + }, }, minTreeWidth: MIN_TREE_WIDTH, maxTreeWidth: MAX_TREE_WIDTH, diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index a5125c3d077..d41d1464166 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -128,9 +128,6 @@ export default { isModeChanged() { return this.diffFile.viewer.name === diffViewerModes.mode_changed; }, - showExpandDiffToFullFileEnabled() { - return gon.features.expandDiffFullFile && !this.diffFile.is_fully_expanded; - }, }, mounted() { polyfillSticky(this.$refs.header); @@ -258,7 +255,7 @@ export default { <icon name="external-link" /> </gl-button> <gl-button - v-if="showExpandDiffToFullFileEnabled" + v-if="!diffFile.is_fully_expanded" class="expand-file js-expand-file" @click="toggleFullDiff(diffFile.file_path)" > diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue index caf0df8a4e3..c60246bf8ef 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue @@ -140,7 +140,7 @@ export default { :id="line.left.line_code" :class="parallelViewLeftLineType" class="line_content parallel left-side" - @mousedown.native="handleParallelLineMouseDown" + @mousedown="handleParallelLineMouseDown" v-html="line.left.rich_text" ></td> </template> @@ -171,7 +171,7 @@ export default { }, ]" class="line_content parallel right-side" - @mousedown.native="handleParallelLineMouseDown" + @mousedown="handleParallelLineMouseDown" v-html="line.right.rich_text" ></td> </template> diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue index 93e754fa896..41a80d99850 100644 --- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue +++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue @@ -38,36 +38,34 @@ export default { </script> <template> - <div + <table :class="$options.userColorScheme" :data-commit-id="commitId" class="code diff-wrap-lines js-syntax-highlight text-file" > - <table> - <tbody> - <template v-for="(line, index) in diffLines"> - <parallel-diff-table-row - :key="line.line_code" - :file-hash="diffFile.file_hash" - :context-lines-path="diffFile.context_lines_path" - :line="line" - :is-bottom="index + 1 === diffLinesLength" - /> - <parallel-diff-comment-row - :key="`dcr-${line.line_code || index}`" - :line="line" - :diff-file-hash="diffFile.file_hash" - :line-index="index" - :help-page-path="helpPagePath" - /> - <parallel-draft-comment-row - v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)" - :key="`drafts-${index}`" - :line="line" - :diff-file-content-sha="diffFile.file_hash" - /> - </template> - </tbody> - </table> - </div> + <tbody> + <template v-for="(line, index) in diffLines"> + <parallel-diff-table-row + :key="line.line_code" + :file-hash="diffFile.file_hash" + :context-lines-path="diffFile.context_lines_path" + :line="line" + :is-bottom="index + 1 === diffLinesLength" + /> + <parallel-diff-comment-row + :key="`dcr-${line.line_code || index}`" + :line="line" + :diff-file-hash="diffFile.file_hash" + :line-index="index" + :help-page-path="helpPagePath" + /> + <parallel-draft-comment-row + v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)" + :key="`drafts-${index}`" + :line="line" + :diff-file-content-sha="diffFile.file_hash" + /> + </template> + </tbody> + </table> </template> diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index 8fc3af15bea..384f33e0983 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -30,8 +30,9 @@ export default { filteredTreeList() { const search = this.search.toLowerCase().trim(); - if (search === '' || this.$options.fuzzyFileFinderEnabled) + if (search === '') { return this.renderTreeList ? this.tree : this.allBlobs; + } return this.allBlobs.reduce((acc, folder) => { const tree = folder.tree.filter(f => f.path.toLowerCase().indexOf(search) >= 0); @@ -51,13 +52,11 @@ export default { }, }, methods: { - ...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile', 'toggleFileFinder']), + ...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile']), clearSearch() { this.search = ''; }, }, - shortcutKeyCharacter: `${/Mac/i.test(navigator.userAgent) ? '⌘' : 'Ctrl'}+P`, - diffTreeFiltering: gon.features && gon.features.diffTreeFiltering, }; </script> @@ -66,36 +65,21 @@ export default { <div class="append-bottom-8 position-relative tree-list-search d-flex"> <div class="flex-fill d-flex"> <icon name="search" class="position-absolute tree-list-icon" /> - <template v-if="$options.diffTreeFiltering"> - <input - v-model="search" - :placeholder="s__('MergeRequest|Filter files')" - type="search" - class="form-control" - /> - <button - v-show="search" - :aria-label="__('Clear search')" - type="button" - class="position-absolute bg-transparent tree-list-icon tree-list-clear-icon border-0 p-0" - @click="clearSearch" - > - <icon name="close" /> - </button> - </template> - <template v-else> - <button - type="button" - class="form-control text-left text-secondary" - @click="toggleFileFinder(true)" - > - {{ s__('MergeRequest|Search files') }} - </button> - <span - class="position-absolute text-secondary diff-tree-search-shortcut" - v-html="$options.shortcutKeyCharacter" - ></span> - </template> + <input + v-model="search" + :placeholder="s__('MergeRequest|Filter files')" + type="search" + class="form-control" + /> + <button + v-show="search" + :aria-label="__('Clear search')" + type="button" + class="position-absolute bg-transparent tree-list-icon tree-list-clear-icon border-0 p-0" + @click="clearSearch" + > + <icon name="close" /> + </button> </div> </div> <div :class="{ 'pt-0 tree-list-blobs': !renderTreeList }" class="tree-list-scroll"> diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 4a04216d893..b58ae0d248c 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -266,9 +266,12 @@ export const scrollToFile = ({ state, commit }, path) => { commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash); }; -export const toggleShowTreeList = ({ commit, state }) => { +export const toggleShowTreeList = ({ commit, state }, saving = true) => { commit(types.TOGGLE_SHOW_TREE_LIST); - localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList); + + if (saving) { + localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList); + } }; export const openDiffFileCommentForm = ({ commit, getters }, formData) => { diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js index 47f78a5db54..cf4dd93dbfb 100644 --- a/app/assets/javascripts/diffs/store/modules/diff_state.js +++ b/app/assets/javascripts/diffs/store/modules/diff_state.js @@ -1,13 +1,10 @@ import Cookies from 'js-cookie'; import { getParameterValues } from '~/lib/utils/url_utility'; -import bp from '~/breakpoints'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, MR_TREE_SHOW_KEY } from '../../constants'; +import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants'; const viewTypeFromQueryString = getParameterValues('view')[0]; const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME); const defaultViewType = INLINE_DIFF_VIEW_TYPE; -const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY); export default () => ({ isLoading: true, @@ -23,8 +20,7 @@ export default () => ({ diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType, tree: [], treeEntries: {}, - showTreeList: - storedTreeShow === null ? bp.getBreakpointSize() !== 'xs' : parseBoolean(storedTreeShow), + showTreeList: true, currentDiffFileId: '', projectPath: '', commentForms: [], diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue index ff4e16178e8..55613d815ce 100644 --- a/app/assets/javascripts/environments/components/environments_table.vue +++ b/app/assets/javascripts/environments/components/environments_table.vue @@ -99,7 +99,7 @@ export default { /> <div - v-if="shouldRenderDeployBoard" + v-if="shouldRenderDeployBoard(model)" :key="`deploy-board-row-${i}`" class="js-deploy-board-row" > diff --git a/app/assets/javascripts/filtered_search/visual_token_value.js b/app/assets/javascripts/filtered_search/visual_token_value.js index 7f6f41c18f7..24532d88cf3 100644 --- a/app/assets/javascripts/filtered_search/visual_token_value.js +++ b/app/assets/javascripts/filtered_search/visual_token_value.js @@ -13,9 +13,9 @@ export default class VisualTokenValue { } render(tokenValueContainer, tokenValueElement) { - const { tokenType } = this; + const { tokenType, tokenValue } = this; - if (['none', 'any'].includes(tokenType)) { + if (['none', 'any'].includes(tokenValue.toLowerCase())) { return; } diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index 29dc2d6a8a3..aa50fd8ff62 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -244,7 +244,7 @@ export default { <gl-loading-icon v-if="isLoading" :label="s__('GroupsTree|Loading groups')" - :size="2" + size="md" class="loading-animation prepend-top-20" /> <groups-component diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue index 2b44438f849..9161eb3d9b1 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue @@ -38,8 +38,8 @@ export default { }, }, computed: { - ...mapState('commit', ['commitAction']), - ...mapGetters('commit', ['newBranchName']), + ...mapState('commit', ['commitAction', 'newBranchName']), + ...mapGetters('commit', ['placeholderBranchName']), tooltipTitle() { return this.disabled ? this.title : ''; }, @@ -73,7 +73,8 @@ export default { </label> <div v-if="commitAction === value && showInput" class="ide-commit-new-branch"> <input - :placeholder="newBranchName" + :placeholder="placeholderBranchName" + :value="newBranchName" type="text" class="form-control monospace" @input="updateBranchName($event.target.value)" diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index ba6bbdfef4b..412b07553dc 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -29,7 +29,7 @@ export default { return this.name || (entryPath ? `${entryPath}/` : ''); }, set(val) { - this.name = val; + this.name = val.trim(); }, }, modalTitle() { diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js index 03777e6c10b..bbe40b2ec2f 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/getters.js +++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js @@ -14,7 +14,7 @@ const createTranslatedTextForFiles = (files, text) => { export const discardDraftButtonDisabled = state => state.commitMessage === '' || state.submitCommitLoading; -export const newBranchName = (state, _, rootState) => +export const placeholderBranchName = (state, _, rootState) => `${gon.current_username}-${rootState.currentBranchId}-patch-${`${new Date().getTime()}`.substr( -BRANCH_SUFFIX_COUNT, )}`; @@ -25,7 +25,7 @@ export const branchName = (state, getters, rootState) => { state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR ) { if (state.newBranchName === '') { - return getters.newBranchName; + return getters.placeholderBranchName; } return state.newBranchName; diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 58f14bac8c8..732184dc782 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -140,7 +140,7 @@ export default { 'issue-realtime-pre-pulse': preAnimation, 'issue-realtime-trigger-pulse': pulseAnimation, }" - class="wiki" + class="md" v-html="descriptionHtml" ></div> <textarea diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue index 7076a79dd5d..b651a6e4bfb 100644 --- a/app/assets/javascripts/jobs/components/commit_block.vue +++ b/app/assets/javascripts/jobs/components/commit_block.vue @@ -39,7 +39,7 @@ export default { </gl-link> <clipboard-button - :text="commit.short_id" + :text="commit.id" :title="__('Copy commit SHA to clipboard')" css-class="btn btn-clipboard btn-transparent" /> diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue index c5076d65ff9..6e92b599b0a 100644 --- a/app/assets/javascripts/jobs/components/stages_dropdown.vue +++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue @@ -1,5 +1,6 @@ <script> import _ from 'underscore'; +import { GlLink } from '@gitlab/ui'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import Icon from '~/vue_shared/components/icon.vue'; @@ -7,6 +8,7 @@ export default { components: { CiIcon, Icon, + GlLink, }, props: { pipeline: { @@ -26,6 +28,12 @@ export default { hasRef() { return !_.isEmpty(this.pipeline.ref); }, + isTriggeredByMergeRequest() { + return Boolean(this.pipeline.merge_request); + }, + isMergeRequestPipeline() { + return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline); + }, }, methods: { onStageClick(stage) { @@ -36,16 +44,41 @@ export default { </script> <template> <div class="block-last dropdown"> - <ci-icon :status="pipeline.details.status" class="vertical-align-middle" /> + <div class="js-pipeline-info"> + <ci-icon :status="pipeline.details.status" class="vertical-align-middle" /> - <span class="font-weight-bold">{{ __('Pipeline') }}</span> - <a :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path" - >#{{ pipeline.id }}</a - > - <template v-if="hasRef"> - {{ __('from') }} - <a :href="pipeline.ref.path" class="link-commit ref-name">{{ pipeline.ref.name }}</a> - </template> + <span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span> + <gl-link :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path" + >#{{ pipeline.id }}</gl-link + > + <template v-if="hasRef"> + {{ s__('Job|for') }} + + <template v-if="isTriggeredByMergeRequest"> + <gl-link :href="pipeline.merge_request.path" class="link-commit ref-name js-mr-link" + >!{{ pipeline.merge_request.iid }}</gl-link + > + {{ s__('Job|with') }} + <gl-link + :href="pipeline.merge_request.source_branch_path" + class="link-commit ref-name js-source-branch-link" + >{{ pipeline.merge_request.source_branch }}</gl-link + > + + <template v-if="isMergeRequestPipeline"> + {{ s__('Job|into') }} + <gl-link + :href="pipeline.merge_request.target_branch_path" + class="link-commit ref-name js-target-branch-link" + >{{ pipeline.merge_request.target_branch }}</gl-link + > + </template> + </template> + <gl-link v-else :href="pipeline.ref.path" class="link-commit ref-name">{{ + pipeline.ref.name + }}</gl-link> + </template> + </div> <button type="button" diff --git a/app/assets/javascripts/lib/utils/autosave.js b/app/assets/javascripts/lib/utils/autosave.js new file mode 100644 index 00000000000..023c336db02 --- /dev/null +++ b/app/assets/javascripts/lib/utils/autosave.js @@ -0,0 +1,32 @@ +import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; + +export const clearDraft = autosaveKey => { + try { + window.localStorage.removeItem(`autosave/${autosaveKey}`); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } +}; + +export const getDraft = autosaveKey => { + try { + return window.localStorage.getItem(`autosave/${autosaveKey}`); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return null; + } +}; + +export const updateDraft = (autosaveKey, text) => { + try { + window.localStorage.setItem(`autosave/${autosaveKey}`, text); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } +}; + +export const getDiscussionReplyKey = (noteableType, discussionId) => + ['Note', capitalizeFirstCharacter(noteableType), discussionId, 'Reply'].join('/'); diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js index c2de0379d23..3cb406b819d 100644 --- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js +++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js @@ -16,7 +16,7 @@ import utilsMixin from '../mixins/line_conflict_utils'; }, }, template: ` - <table> + <table class="diff-wrap-lines code js-syntax-highlight"> <tr class="line_holder parallel" v-for="section in file.parallelLines"> <template v-for="line in section"> <td class="diff-line-num header" :class="lineCssClass(line)" v-if="line.isHeader"></td> diff --git a/app/assets/javascripts/mirrors/ssh_mirror.js b/app/assets/javascripts/mirrors/ssh_mirror.js index 5bdf5d6277a..547c078ec55 100644 --- a/app/assets/javascripts/mirrors/ssh_mirror.js +++ b/app/assets/javascripts/mirrors/ssh_mirror.js @@ -20,6 +20,7 @@ export default class SSHMirror { this.$btnDetectHostKeys = this.$form.find('.js-detect-host-keys'); this.$btnSSHHostsShowAdvanced = this.$form.find('.btn-show-advanced'); this.$dropdownAuthType = this.$form.find('.js-mirror-auth-type'); + this.$hiddenAuthType = this.$form.find('.js-hidden-mirror-auth-type'); this.$wellAuthTypeChanging = this.$form.find('.js-well-changing-auth'); this.$wellPasswordAuth = this.$form.find('.js-well-password-auth'); @@ -167,6 +168,7 @@ export default class SSHMirror { this.$wellPasswordAuth.collapse('hide'); this.$wellSSHAuth.collapse('hide'); + this.updateHiddenAuthType(selectedAuthType); // This request should happen only if selected Auth type was SSH // and SSH Public key was not present on page load @@ -234,6 +236,12 @@ export default class SSHMirror { toggleAuthWell(authType) { this.$wellPasswordAuth.collapse(authType === AUTH_METHOD.PASSWORD ? 'show' : 'hide'); this.$wellSSHAuth.collapse(authType === AUTH_METHOD.SSH ? 'show' : 'hide'); + this.updateHiddenAuthType(authType); + } + + updateHiddenAuthType(authType) { + this.$hiddenAuthType.val(authType); + this.$hiddenAuthType.prop('disabled', authType === AUTH_METHOD.SSH); } /** diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 895a57785bc..ba6a17827f7 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -1,6 +1,8 @@ <script> +import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { s__ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; +import '~/vue_shared/mixins/is_ee'; import Flash from '../../flash'; import MonitoringService from '../services/monitoring_service'; import MonitorAreaChart from './charts/area.vue'; @@ -17,7 +19,10 @@ export default { GraphGroup, EmptyState, Icon, + GlDropdown, + GlDropdownItem, }, + props: { hasMetrics: { type: Boolean, @@ -104,9 +109,29 @@ export default { } }, mounted() { + this.servicePromises = [ + this.service + .getGraphsData() + .then(data => this.store.storeMetrics(data)) + .catch(() => Flash(s__('Metrics|There was an error while retrieving metrics'))), + this.service + .getDeploymentData() + .then(data => this.store.storeDeploymentData(data)) + .catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))), + ]; if (!this.hasMetrics) { this.state = 'gettingStarted'; } else { + if (this.environmentsEndpoint) { + this.servicePromises.push( + this.service + .getEnvironmentsData() + .then(data => this.store.storeEnvironmentsData(data)) + .catch(() => + Flash(s__('Metrics|There was an error getting environments information.')), + ), + ); + } this.getGraphsData(); sidebarMutationObserver = new MutationObserver(this.onSidebarMutation); sidebarMutationObserver.observe(document.querySelector('.layout-page'), { @@ -122,17 +147,7 @@ export default { }, getGraphsData() { this.state = 'loading'; - Promise.all([ - this.service.getGraphsData().then(data => this.store.storeMetrics(data)), - this.service - .getDeploymentData() - .then(data => this.store.storeDeploymentData(data)) - .catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))), - this.service - .getEnvironmentsData() - .then(data => this.store.storeEnvironmentsData(data)) - .catch(() => Flash(s__('Metrics|There was an error getting environments information.'))), - ]) + Promise.all(this.servicePromises) .then(() => { if (this.store.groups.length < 1) { this.state = 'noData'; @@ -156,29 +171,22 @@ export default { <template> <div v-if="!showEmptyState" class="prometheus-graphs prepend-top-default"> - <div class="environments d-flex align-items-center"> - {{ s__('Metrics|Environment') }} - <div class="dropdown prepend-left-10"> - <button class="dropdown-menu-toggle" data-toggle="dropdown" type="button"> - <span>{{ currentEnvironmentName }}</span> - <icon name="chevron-down" /> - </button> - <div - v-if="store.environmentsData.length > 0" - class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up" + <div v-if="environmentsEndpoint" class="environments d-flex align-items-center"> + <strong>{{ s__('Metrics|Environment') }}</strong> + <gl-dropdown + class="prepend-left-10 js-environments-dropdown" + toggle-class="dropdown-menu-toggle" + :text="currentEnvironmentName" + :disabled="store.environmentsData.length === 0" + > + <gl-dropdown-item + v-for="environment in store.environmentsData" + :key="environment.id" + :active="environment.name === currentEnvironmentName" + active-class="is-active" + >{{ environment.name }}</gl-dropdown-item > - <ul> - <li v-for="environment in store.environmentsData" :key="environment.id"> - <a - :href="environment.metrics_path" - :class="{ 'is-active': environment.name == currentEnvironmentName }" - class="dropdown-item" - >{{ environment.name }}</a - > - </li> - </ul> - </div> - </div> + </gl-dropdown> </div> <graph-group v-for="(groupData, index) in store.groups" @@ -194,7 +202,17 @@ export default { :alert-data="getGraphAlerts(graphData.id)" :container-width="elWidth" group-id="monitor-area-chart" - /> + > + <alert-widget + v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData.id" + :alerts-endpoint="alertsEndpoint" + :label="getGraphLabel(graphData)" + :current-alerts="getQueryAlerts(graphData)" + :custom-metric-id="graphData.id" + :alert-data="alertData[graphData.id]" + @setAlerts="setAlerts" + /> + </monitor-area-chart> </graph-group> </div> <empty-state diff --git a/app/assets/javascripts/mr_popover/components/mr_popover.vue b/app/assets/javascripts/mr_popover/components/mr_popover.vue new file mode 100644 index 00000000000..8e2d8fa816a --- /dev/null +++ b/app/assets/javascripts/mr_popover/components/mr_popover.vue @@ -0,0 +1,110 @@ +<script> +import { GlPopover, GlSkeletonLoading } from '@gitlab/ui'; +import Icon from '../../vue_shared/components/icon.vue'; +import CiIcon from '../../vue_shared/components/ci_icon.vue'; +import timeagoMixin from '../../vue_shared/mixins/timeago'; +import query from '../queries/merge_request.graphql'; +import { mrStates, humanMRStates } from '../constants'; + +export default { + name: 'MRPopover', + components: { + GlPopover, + GlSkeletonLoading, + Icon, + CiIcon, + }, + mixins: [timeagoMixin], + props: { + target: { + type: HTMLAnchorElement, + required: true, + }, + projectPath: { + type: String, + required: true, + }, + mergeRequestIID: { + type: String, + required: true, + }, + mergeRequestTitle: { + type: String, + required: true, + }, + }, + data() { + return { + mergeRequest: {}, + }; + }, + computed: { + detailedStatus() { + return this.mergeRequest.headPipeline && this.mergeRequest.headPipeline.detailedStatus; + }, + formattedTime() { + return this.timeFormated(this.mergeRequest.createdAt); + }, + statusBoxClass() { + switch (this.mergeRequest.state) { + case mrStates.merged: + return 'status-box-mr-merged'; + case mrStates.closed: + return 'status-box-closed'; + default: + return 'status-box-open'; + } + }, + stateHumanName() { + switch (this.mergeRequest.state) { + case mrStates.merged: + return humanMRStates.merged; + case mrStates.closed: + return humanMRStates.closed; + default: + return humanMRStates.open; + } + }, + showDetails() { + return Object.keys(this.mergeRequest).length > 0; + }, + }, + apollo: { + mergeRequest: { + query, + update: data => data.project.mergeRequest, + variables() { + const { projectPath, mergeRequestIID } = this; + + return { + projectPath, + mergeRequestIID, + }; + }, + }, + }, +}; +</script> + +<template> + <gl-popover :target="target" boundary="viewport" placement="top" show> + <div class="mr-popover"> + <div v-if="$apollo.loading"> + <gl-skeleton-loading :lines="1" class="animation-container-small mt-1" /> + </div> + <div v-else-if="showDetails" class="d-flex align-items-center justify-content-between"> + <div class="d-inline-flex align-items-center"> + <div :class="`issuable-status-box status-box ${statusBoxClass}`"> + {{ stateHumanName }} + </div> + <span class="text-secondary">Opened <time v-text="formattedTime"></time></span> + </div> + <ci-icon v-if="detailedStatus" :status="detailedStatus" /> + </div> + <h5 class="my-2">{{ mergeRequestTitle }}</h5> + <div class="text-secondary"> + {{ `${projectPath}!${mergeRequestIID}` }} + </div> + </div> + </gl-popover> +</template> diff --git a/app/assets/javascripts/mr_popover/constants.js b/app/assets/javascripts/mr_popover/constants.js new file mode 100644 index 00000000000..433df844c80 --- /dev/null +++ b/app/assets/javascripts/mr_popover/constants.js @@ -0,0 +1,10 @@ +export const mrStates = { + merged: 'merged', + closed: 'closed', +}; + +export const humanMRStates = { + merged: 'Merged', + closed: 'Closed', + open: 'Open', +}; diff --git a/app/assets/javascripts/mr_popover/index.js b/app/assets/javascripts/mr_popover/index.js new file mode 100644 index 00000000000..cc686b401d2 --- /dev/null +++ b/app/assets/javascripts/mr_popover/index.js @@ -0,0 +1,62 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import MRPopover from './components/mr_popover.vue'; +import createDefaultClient from '~/lib/graphql'; + +let renderedPopover; +let renderFn; + +const handleUserPopoverMouseOut = ({ target }) => { + target.removeEventListener('mouseleave', handleUserPopoverMouseOut); + + if (renderFn) { + clearTimeout(renderFn); + } + if (renderedPopover) { + renderedPopover.$destroy(); + renderedPopover = null; + } +}; + +/** + * Adds a MergeRequestPopover component to the body, hands over as much data as the target element has in data attributes. + * loads based on data-project-path and data-iid more data about an MR from the API and sets it on the popover + */ +const handleMRPopoverMount = apolloProvider => ({ target }) => { + // Add listener to actually remove it again + target.addEventListener('mouseleave', handleUserPopoverMouseOut); + + const { projectPath, mrTitle, iid } = target.dataset; + const mergeRequest = {}; + + renderFn = setTimeout(() => { + const MRPopoverComponent = Vue.extend(MRPopover); + renderedPopover = new MRPopoverComponent({ + propsData: { + target, + projectPath, + mergeRequestIID: iid, + mergeRequest, + mergeRequestTitle: mrTitle, + }, + apolloProvider, + }); + + renderedPopover.$mount(); + }, 200); // 200ms delay so not every mouseover triggers Popover + API Call +}; + +export default elements => { + const mrLinks = elements || [...document.querySelectorAll('.gfm-merge_request')]; + if (mrLinks.length > 0) { + Vue.use(VueApollo); + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), + }); + + mrLinks.forEach(el => { + el.addEventListener('mouseenter', handleMRPopoverMount(apolloProvider)); + }); + } +}; diff --git a/app/assets/javascripts/mr_popover/queries/merge_request.graphql b/app/assets/javascripts/mr_popover/queries/merge_request.graphql new file mode 100644 index 00000000000..0bb9bc03bc7 --- /dev/null +++ b/app/assets/javascripts/mr_popover/queries/merge_request.graphql @@ -0,0 +1,14 @@ +query mergeRequest($projectPath: ID!, $mergeRequestIID: ID!) { + project(fullPath: $projectPath) { + mergeRequest(iid: $mergeRequestIID) { + createdAt + state + headPipeline { + detailedStatus { + icon + group + } + } + } + } +} diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue index c5ae7e7ee10..b59ddd0d57a 100644 --- a/app/assets/javascripts/notebook/cells/output/index.vue +++ b/app/assets/javascripts/notebook/cells/output/index.vue @@ -20,12 +20,20 @@ export default { required: true, }, }, - data() { - return { - outputType: '', - }; - }, methods: { + outputType(output) { + if (output.text) { + return 'text/plain'; + } else if (output.data['image/png']) { + return 'image/png'; + } else if (output.data['text/html']) { + return 'text/html'; + } else if (output.data['image/svg+xml']) { + return 'image/svg+xml'; + } + + return 'text/plain'; + }, dataForType(output, type) { let data = output.data[type]; @@ -39,20 +47,13 @@ export default { if (output.text) { return CodeOutput; } else if (output.data['image/png']) { - this.outputType = 'image/png'; - return ImageOutput; } else if (output.data['text/html']) { - this.outputType = 'text/html'; - return HtmlOutput; } else if (output.data['image/svg+xml']) { - this.outputType = 'image/svg+xml'; - return HtmlOutput; } - this.outputType = 'text/plain'; return CodeOutput; }, rawCode(output) { @@ -60,7 +61,7 @@ export default { return output.text.join(''); } - return this.dataForType(output, this.outputType); + return this.dataForType(output, this.outputType(output)); }, }, }; @@ -73,7 +74,7 @@ export default { v-for="(output, index) in outputs" :key="index" type="output" - :output-type="outputType" + :output-type="outputType(output)" :count="count" :index="index" :raw-code="rawCode(output)" diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index d8947e8ca50..ab758a9e922 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -72,8 +72,8 @@ export default { :can-current-user-fork="false" :expanded="!discussion.diff_file.viewer.collapsed" /> - <div v-if="isTextFile" :class="$options.userColorSchemeClass" class="diff-content code"> - <table> + <div v-if="isTextFile" class="diff-content"> + <table class="code js-syntax-highlight" :class="$options.userColorSchemeClass"> <template v-if="hasTruncatedDiffLines"> <tr v-for="line in discussion.truncated_diff_lines" @@ -81,8 +81,8 @@ export default { :key="line.line_code" class="line_holder" > - <td class="diff-line-num old_line">{{ line.old_line }}</td> - <td class="diff-line-num new_line">{{ line.new_line }}</td> + <td :class="line.type" class="diff-line-num old_line">{{ line.old_line }}</td> + <td :class="line.type" class="diff-line-num new_line">{{ line.new_line }}</td> <td :class="line.type" class="line_content" v-html="line.rich_text"></td> </tr> </template> diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue index 31164f74201..47951591e82 100644 --- a/app/assets/javascripts/notes/components/discussion_filter.vue +++ b/app/assets/javascripts/notes/components/discussion_filter.vue @@ -22,7 +22,7 @@ export default { }, selectedValue: { type: Number, - default: null, + default: DISCUSSION_FILTERS_DEFAULT_VALUE, required: false, }, }, diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue index f50cab81efe..be8e42af9ea 100644 --- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue +++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue @@ -18,7 +18,7 @@ export default { <div class="note-actions-item"> <gl-button ref="button" - v-gl-tooltip.bottom + v-gl-tooltip class="note-action-button" variant="transparent" :title="__('Reply to comment')" diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index fb1d98355b3..ff303d0f55a 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -95,7 +95,6 @@ export default { <div ref="note-body" :class="{ 'js-task-list-container': canEdit }" class="note-body"> <suggestions v-if="hasSuggestion && !isEditing" - class="note-text md" :suggestions="note.suggestions" :note-html="note.note_html" :line-type="lineType" diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index 92258a25438..57d6b181bd7 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -7,6 +7,7 @@ import markdownField from '../../vue_shared/components/markdown/field.vue'; import issuableStateMixin from '../mixins/issuable_state'; import resolvable from '../mixins/resolvable'; import { __ } from '~/locale'; +import { getDraft, updateDraft } from '~/lib/utils/autosave'; export default { name: 'NoteForm', @@ -65,10 +66,21 @@ export default { required: false, default: '', }, + autosaveKey: { + type: String, + required: false, + default: '', + }, }, data() { + let updatedNoteBody = this.noteBody; + + if (!updatedNoteBody && this.autosaveKey) { + updatedNoteBody = getDraft(this.autosaveKey) || ''; + } + return { - updatedNoteBody: this.noteBody, + updatedNoteBody, conflictWhileEditing: false, isSubmitting: false, isResolving: this.resolveDiscussion, @@ -175,6 +187,12 @@ export default { // Sends information about confirm message and if the textarea has changed this.$emit('cancelForm', shouldConfirm, this.noteBody !== this.updatedNoteBody); }, + onInput() { + if (this.autosaveKey) { + const { autosaveKey, updatedNoteBody: text } = this; + updateDraft(autosaveKey, text); + } + }, }, }; </script> @@ -218,6 +236,7 @@ export default { @keydown.ctrl.enter="handleKeySubmit()" @keydown.up="editMyLastNote()" @keydown.esc="cancelHandler(true)" + @input="onInput" ></textarea> </markdown-field> <div class="note-form-actions clearfix"> diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index fc51998935d..a3d664a738f 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -4,6 +4,7 @@ import { mapActions, mapGetters } from 'vuex'; import { GlTooltipDirective } from '@gitlab/ui'; import { truncateSha } from '~/lib/utils/text_utility'; import { s__, __, sprintf } from '~/locale'; +import { clearDraft, getDiscussionReplyKey } from '~/lib/utils/autosave'; import systemNote from '~/vue_shared/components/notes/system_note.vue'; import icon from '~/vue_shared/components/icon.vue'; import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form'; @@ -21,7 +22,6 @@ import noteForm from './note_form.vue'; import diffWithNote from './diff_with_note.vue'; import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue'; import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue'; -import autosave from '../mixins/autosave'; import noteable from '../mixins/noteable'; import resolvable from '../mixins/resolvable'; import discussionNavigation from '../mixins/discussion_navigation'; @@ -54,7 +54,7 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - mixins: [autosave, noteable, resolvable, discussionNavigation, diffLineNoteFormMixin], + mixins: [noteable, resolvable, discussionNavigation, diffLineNoteFormMixin], props: { discussion: { type: Object, @@ -87,13 +87,10 @@ export default { }, }, data() { - const { diff_discussion: isDiffDiscussion, resolved } = this.discussion; - return { isReplying: false, isResolving: false, resolveAsThread: true, - isRepliesCollapsed: Boolean(!isDiffDiscussion && resolved), }; }, computed: { @@ -106,7 +103,10 @@ export default { 'showJumpToNextDiscussion', ]), author() { - return this.initialDiscussion.author; + return this.firstNote.author; + }, + autosaveKey() { + return getDiscussionReplyKey(this.firstNote.noteable_type, this.discussion.id); }, canReply() { return this.getNoteableData.current_user.can_create_note; @@ -117,7 +117,7 @@ export default { hasReplies() { return this.discussion.notes.length > 1; }, - initialDiscussion() { + firstNote() { return this.discussion.notes.slice(0, 1)[0]; }, replies() { @@ -175,11 +175,11 @@ export default { return ''; }, - shouldShowDiscussions() { - const { expanded, resolved } = this.discussion; - const isResolvedNonDiffDiscussion = !this.discussion.diff_discussion && resolved; - - return expanded || this.alwaysExpanded || isResolvedNonDiffDiscussion; + isExpanded() { + return this.discussion.expanded || this.alwaysExpanded; + }, + shouldHideDiscussionBody() { + return this.shouldRenderDiffs && !this.isExpanded; }, actionText() { const linkStart = `<a href="${_.escape(this.discussion.discussion_path)}">`; @@ -242,18 +242,6 @@ export default { return !this.discussionResolved && this.discussion.resolve_with_issue_path; }, }, - watch: { - isReplying() { - if (this.isReplying) { - this.$nextTick(() => { - // Pass an extra key to separate reply and note edit forms - this.initAutoSave({ ...this.initialDiscussion, ...this.discussion }, ['Reply']); - }); - } else { - this.disposeAutoSave(); - } - }, - }, created() { eventHub.$on('startReplying', this.onStartReplying); }, @@ -291,9 +279,6 @@ export default { toggleDiscussionHandler() { this.toggleDiscussion({ discussionId: this.discussion.id }); }, - toggleReplies() { - this.isRepliesCollapsed = !this.isRepliesCollapsed; - }, showReplyForm() { this.isReplying = true; }, @@ -312,7 +297,7 @@ export default { } this.isReplying = false; - this.resetAutoSave(); + clearDraft(this.autosaveKey); }, saveReply(noteText, form, callback) { const postData = { @@ -338,7 +323,7 @@ export default { this.isReplying = false; this.saveNote(replyData) .then(() => { - this.resetAutoSave(); + clearDraft(this.autosaveKey); callback(); }) .catch(err => { @@ -390,8 +375,8 @@ Please check your network connection and try again.`; <div class="timeline-content"> <note-header :author="author" - :created-at="initialDiscussion.created_at" - :note-id="initialDiscussion.id" + :created-at="firstNote.created_at" + :note-id="firstNote.id" :include-toggle="true" :expanded="discussion.expanded" @toggleHandler="toggleDiscussionHandler" @@ -414,7 +399,7 @@ Please check your network connection and try again.`; /> </div> </div> - <div v-if="shouldShowDiscussions" class="discussion-body"> + <div v-if="!shouldHideDiscussionBody" class="discussion-body"> <component :is="wrapperComponent" v-bind="wrapperComponentProps" @@ -424,8 +409,8 @@ Please check your network connection and try again.`; <ul class="notes"> <template v-if="shouldGroupReplies"> <component - :is="componentName(initialDiscussion)" - :note="componentData(initialDiscussion)" + :is="componentName(firstNote)" + :note="componentData(firstNote)" :line="line" :commit="commit" :help-page-path="helpPagePath" @@ -445,11 +430,11 @@ Please check your network connection and try again.`; </component> <toggle-replies-widget v-if="hasReplies" - :collapsed="isRepliesCollapsed" + :collapsed="!isExpanded" :replies="replies" - @toggle="toggleReplies" + @toggle="toggleDiscussionHandler" /> - <template v-if="!isRepliesCollapsed"> + <template v-if="isExpanded"> <component :is="componentName(note)" v-for="note in replies" @@ -476,7 +461,7 @@ Please check your network connection and try again.`; </template> </ul> <div - v-if="!isRepliesCollapsed || !hasReplies" + v-if="isExpanded || !hasReplies" :class="{ 'is-replying': isReplying }" class="discussion-reply-holder" > @@ -512,6 +497,7 @@ Please check your network connection and try again.`; :is-editing="false" :line="diffLine" save-button-title="Comment" + :autosave-key="autosaveKey" @handleFormUpdateAddToReview="addReplyToReview" @handleFormUpdate="saveReply" @cancelForm="cancelReplyForm" diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js index 5c5f38a3fb0..cdf9a46c5aa 100644 --- a/app/assets/javascripts/notes/discussion_filters.js +++ b/app/assets/javascripts/notes/discussion_filters.js @@ -6,12 +6,16 @@ export default store => { if (discussionFilterEl) { const { defaultFilter, notesFilters } = discussionFilterEl.dataset; - const selectedValue = defaultFilter ? parseInt(defaultFilter, 10) : null; const filterValues = notesFilters ? JSON.parse(notesFilters) : {}; const filters = Object.keys(filterValues).map(entry => ({ title: entry, value: filterValues[entry], })); + const props = { filters }; + + if (defaultFilter) { + props.selectedValue = parseInt(defaultFilter, 10); + } return new Vue({ el: discussionFilterEl, @@ -21,12 +25,7 @@ export default store => { }, store, render(createElement) { - return createElement('discussion-filter', { - props: { - filters, - selectedValue, - }, - }); + return createElement('discussion-filter', { props }); }, }); } diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index 4883266dae5..30372103590 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -6,9 +6,8 @@ import createStore from './stores'; document.addEventListener('DOMContentLoaded', () => { const store = createStore(); - initDiscussionFilters(store); - - return new Vue({ + // eslint-disable-next-line no-new + new Vue({ el: '#js-vue-notes', components: { notesApp, @@ -49,4 +48,6 @@ document.addEventListener('DOMContentLoaded', () => { }); }, }); + + initDiscussionFilters(store); }); diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index e7fa05faa8a..6aed2492084 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -4,6 +4,7 @@ import Flash from './flash'; export default function notificationsDropdown() { $(document).on('click', '.update-notification', function updateNotificationCallback(e) { e.preventDefault(); + if ($(this).is('.is-active') && $(this).data('notificationLevel') === 'custom') { return; } diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue index 918622ef8dc..3e7bf20470c 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_url.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue @@ -110,12 +110,12 @@ export default { {{ __('stuck') }} </span> <span - v-if="pipeline.flags.merge_request" + v-if="pipeline.flags.detached_merge_request_pipeline" v-gl-tooltip - :title="__('This pipeline is run in a merge request context')" - class="js-pipeline-url-mergerequest badge badge-info" + :title="__('This pipeline is run on the source branch')" + class="js-pipeline-url-detached badge badge-info" > - {{ __('merge request') }} + {{ __('detached') }} </span> </div> </div> diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index f6454a84ea5..1c44427e720 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -272,10 +272,11 @@ export default { :tag="commitTag" :commit-ref="commitRef" :commit-url="commitUrl" + :merge-request-ref="pipeline.merge_request" :short-sha="commitShortSha" :title="commitTitle" :author="commitAuthor" - :show-branch="!isChildView" + :show-ref-info="!isChildView" /> </div> </div> diff --git a/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js b/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js index 9177943f88a..dd79ade5bc9 100644 --- a/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js +++ b/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js @@ -1,6 +1,16 @@ +import Flash from '~/flash'; +import { __ } from '~/locale'; + export default { methods: { clickTriggeredByPipeline() {}, clickTriggeredPipeline() {}, + requestRefreshPipelineGraph() { + // When an action is clicked + // (wether in the dropdown or in the main nodes, we refresh the big graph) + this.mediator + .refreshPipeline() + .catch(() => Flash(__('An error occurred while making the request.'))); + }, }, }; diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index 8adbd39edd4..6660f8120f8 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -29,15 +29,6 @@ export default () => { mediator, }; }, - methods: { - requestRefreshPipelineGraph() { - // When an action is clicked - // (wether in the dropdown or in the main nodes, we refresh the big graph) - this.mediator - .refreshPipeline() - .catch(() => Flash(__('An error occurred while making the request.'))); - }, - }, render(createElement) { return createElement('pipeline-graph', { props: { diff --git a/app/assets/javascripts/pipelines/stores/pipeline_store.js b/app/assets/javascripts/pipelines/stores/pipeline_store.js index 052e34a8aef..259278b6410 100644 --- a/app/assets/javascripts/pipelines/stores/pipeline_store.js +++ b/app/assets/javascripts/pipelines/stores/pipeline_store.js @@ -1,7 +1,6 @@ export default class PipelineStore { constructor() { this.state = {}; - this.state.pipeline = {}; } 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 9e63aa00341..f5a1ff2f6fd 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 @@ -1,5 +1,6 @@ <script> /* eslint-disable vue/require-default-prop */ +import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import { sprintf, __ } from '~/locale'; import PipelineStage from '~/pipelines/components/stage.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; @@ -14,9 +15,13 @@ export default { CiIcon, Icon, TooltipOnTruncate, + GlLink, LinkedPipelinesMiniList: () => import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, + directives: { + GlTooltip: GlTooltipDirective, + }, mixins: [mrWidgetPipelineMixin], props: { pipeline: { @@ -78,12 +83,18 @@ export default { false, ); }, + isTriggeredByMergeRequest() { + return Boolean(this.pipeline.merge_request); + }, + isMergeRequestPipeline() { + return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline); + }, }, }; </script> <template> - <div v-if="hasPipeline || hasCIError" class="ci-widget media"> + <div v-if="hasPipeline || hasCIError" class="ci-widget media js-ci-widget"> <template v-if="hasCIError"> <div class="add-border ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-default" @@ -99,21 +110,58 @@ export default { <div class="ci-widget-container d-flex"> <div class="ci-widget-content"> <div class="media-body"> - <div class="font-weight-bold"> - Pipeline - <a :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number" - >#{{ pipeline.id }}</a + <div class="font-weight-bold js-pipeline-info-container"> + {{ s__('Pipeline|Pipeline') }} + <gl-link :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number" + >#{{ pipeline.id }}</gl-link > {{ pipeline.details.status.label }} <template v-if="hasCommitInfo"> - for - <a + {{ s__('Pipeline|for') }} + <gl-link :href="pipeline.commit.commit_path" class="commit-sha js-commit-link font-weight-normal" - >{{ pipeline.commit.short_id }}</a + >{{ pipeline.commit.short_id }}</gl-link > - on + {{ s__('Pipeline|on') }} + <template v-if="isTriggeredByMergeRequest"> + <gl-link + v-gl-tooltip + :href="pipeline.merge_request.path" + :title="pipeline.merge_request.title" + class="font-weight-normal" + >!{{ pipeline.merge_request.iid }}</gl-link + > + {{ s__('Pipeline|with') }} + <tooltip-on-truncate + :title="pipeline.merge_request.source_branch" + truncate-target="child" + class="label-branch label-truncate" + > + <gl-link + :href="pipeline.merge_request.source_branch_path" + class="font-weight-normal" + >{{ pipeline.merge_request.source_branch }}</gl-link + > + </tooltip-on-truncate> + + <template v-if="isMergeRequestPipeline"> + {{ s__('Pipeline|into') }} + <tooltip-on-truncate + :title="pipeline.merge_request.target_branch" + truncate-target="child" + class="label-branch label-truncate" + > + <gl-link + :href="pipeline.merge_request.target_branch_path" + class="font-weight-normal" + >{{ pipeline.merge_request.target_branch }}</gl-link + > + </tooltip-on-truncate> + </template> + </template> <tooltip-on-truncate + v-else :title="sourceBranch" truncate-target="child" class="label-branch label-truncate" @@ -121,7 +169,9 @@ export default { /> </template> </div> - <div v-if="pipeline.coverage" class="coverage">Coverage {{ pipeline.coverage }}%</div> + <div v-if="pipeline.coverage" class="coverage"> + {{ s__('Pipeline|Coverage') }} {{ pipeline.coverage }}% + </div> </div> </div> <div> 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 2a4dff71d9b..11bc8c73ee9 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 @@ -80,7 +80,7 @@ export default { <status-icon :show-disabled-button="true" status="warning" /> <div class="media-body space-children"> <span class="bold"> - <span v-if="mr.mergeError" class="has-error-message"> {{ mergeError }}. </span> + <span v-if="mr.mergeError" class="has-error-message"> {{ mergeError }} </span> <span v-else> {{ s__('mrWidget|Merge failed.') }} </span> <span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span> </span> diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue index e6f0a1c69cd..25f80219993 100644 --- a/app/assets/javascripts/vue_shared/components/ci_icon.vue +++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue @@ -22,6 +22,7 @@ import Icon from '../../vue_shared/components/icon.vue'; * - Jobs show view header * - Jobs show view sidebar * - Linked pipelines + * - Extended MR Popover */ const validSizes = [8, 12, 16, 18, 24, 32, 48, 72]; diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue index ee685a4b8cd..3f282138bdf 100644 --- a/app/assets/javascripts/vue_shared/components/commit.vue +++ b/app/assets/javascripts/vue_shared/components/commit.vue @@ -1,5 +1,6 @@ <script> -import { GlTooltipDirective } from '@gitlab/ui'; +import _ from 'underscore'; +import { GlTooltipDirective, GlLink } from '@gitlab/ui'; import UserAvatarLink from './user_avatar/user_avatar_link.vue'; import Icon from '../../vue_shared/components/icon.vue'; @@ -10,6 +11,7 @@ export default { components: { UserAvatarLink, Icon, + GlLink, }, props: { /** @@ -33,6 +35,27 @@ export default { required: false, default: () => ({}), }, + + /** + * If provided, is used the render the MR IID and link + * in place of the branch name. Must contains the + * following properties: + * - iid (number) + * - path (non-empty string) + * + * May optionally contain the following properties: + * - title (string): used in a tooltip if provided + * + * Any additional properties are ignored. + */ + mergeRequestRef: { + type: Object, + required: false, + default: undefined, + validator: ref => + _.isUndefined(ref) || (_.isFinite(ref.iid) && _.isString(ref.path) && !_.isEmpty(ref.path)), + }, + /** * Used to link to the commit sha. */ @@ -70,7 +93,11 @@ export default { required: false, default: () => ({}), }, - showBranch: { + + /** + * Indicates whether or not to show the branch/MR ref info + */ + showRefInfo: { type: Boolean, required: false, default: true, @@ -78,14 +105,12 @@ export default { }, computed: { /** - * Used to verify if all the properties needed to render the commit - * ref section were provided. - * - * @returns {Boolean} + * Determines if we shoud render the ref info section based */ - hasCommitRef() { - return this.commitRef && this.commitRef.name && this.commitRef.ref_url; + shouldShowRefInfo() { + return this.showRefInfo && (this.commitRef || this.mergeRequestRef); }, + /** * Used to verify if all the properties needed to render the commit * author section were provided. @@ -109,18 +134,35 @@ export default { </script> <template> <div class="branch-commit"> - <template v-if="hasCommitRef && showBranch"> + <template v-if="shouldShowRefInfo"> <div class="icon-container"> - <i v-if="tag" class="fa fa-tag" aria-hidden="true"> </i> <icon v-if="!tag" name="fork" /> + <icon v-if="tag" name="tag" /> + <icon v-else-if="mergeRequestRef" name="git-merge" /> + <icon v-else name="branch" /> </div> - <a v-gl-tooltip :href="commitRef.ref_url" :title="commitRef.name" class="ref-name"> + <gl-link + v-if="mergeRequestRef" + v-gl-tooltip + :href="mergeRequestRef.path" + :title="mergeRequestRef.title" + class="ref-name" + > + {{ mergeRequestRef.iid }} + </gl-link> + <gl-link + v-else + v-gl-tooltip + :href="commitRef.ref_url" + :title="commitRef.name" + class="ref-name" + > {{ commitRef.name }} - </a> + </gl-link> </template> <icon name="commit" class="commit-icon js-commit-icon" /> - <a :href="commitUrl" class="commit-sha"> {{ shortSha }} </a> + <gl-link :href="commitUrl" class="commit-sha"> {{ shortSha }} </gl-link> <div class="commit-title flex-truncate-parent"> <span v-if="title" class="flex-truncate-child"> @@ -132,7 +174,7 @@ export default { :tooltip-text="author.username" class="avatar-image-container" /> - <a :href="commitUrl" class="commit-row-message"> {{ title }} </a> + <gl-link :href="commitUrl" class="commit-row-message"> {{ title }} </gl-link> </span> <span v-else> Can't find HEAD commit for this branch </span> </div> diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue index c9915f7d685..5fdc915fffb 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue @@ -78,8 +78,8 @@ export default { </script> <template> - <div ref="markdown-preview" class="md md-previewer"> + <div ref="markdown-preview" class="md-previewer"> <gl-skeleton-loading v-if="isLoading" /> - <div v-else v-html="previewContent"></div> + <div v-else class="md" v-html="previewContent"></div> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue index 7a53d053eec..216f6c62e69 100644 --- a/app/assets/javascripts/vue_shared/components/loading_button.vue +++ b/app/assets/javascripts/vue_shared/components/loading_button.vue @@ -53,7 +53,7 @@ export default { <template> <button :class="containerClass" :disabled="loading || disabled" type="button" @click="onClick"> - <transition name="fade"> + <transition name="fade-in"> <gl-loading-icon v-if="loading" :inline="true" @@ -63,7 +63,7 @@ export default { class="js-loading-button-icon" /> </transition> - <transition name="fade"> + <transition name="fade-in"> <slot> <span v-if="label" class="js-loading-button-label"> {{ label }} </span> </slot> diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 3f607aa2a0a..a4b3131c8e4 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -215,7 +215,7 @@ export default { <div v-show="previewMarkdown" ref="markdown-preview" - class="md-preview js-vue-md-preview md md-preview-holder" + class="js-vue-md-preview md-preview-holder" > <suggestions v-if="hasSuggestion" @@ -233,7 +233,7 @@ export default { <div v-show="previewMarkdown" ref="markdown-preview" - class="md-preview js-vue-md-preview md md-preview-holder" + class="js-vue-md-preview md md-preview-holder" v-html="markdownPreview" ></div> </template> diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index dcda701f049..177d78cb904 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -130,6 +130,6 @@ export default { <template> <div> <div class="flash-container js-suggestions-flash"></div> - <div v-show="isRendered" ref="container" v-html="noteHtml"></div> + <div v-show="isRendered" ref="container" class="md" v-html="noteHtml"></div> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue index 8d3a3009c55..a50f49c1279 100644 --- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue @@ -57,7 +57,7 @@ export default { </div> </div> <div class="note-body"> - <div class="note-text"> + <div class="note-text md"> <p>{{ note.body }}</p> </div> </div> diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index b0af8399955..acc179b3834 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -93,7 +93,7 @@ export default { 'system-note-commit-list': hasMoreCommits, 'hide-shade': expanded, }" - class="note-text" + class="note-text md" v-html="note.note_html" ></div> <div v-if="hasMoreCommits" class="flex-list"> diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index c8357f7751c..93377b8dd91 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -343,16 +343,6 @@ input[type=color].form-control { } } -// Bootstrap 3 compatibility because bootstrap_form Gem is not updated yet -.input-group-btn:first-child { - @extend .input-group-prepend; -} - -// Bootstrap 3 compatibility because bootstrap_form Gem is not updated yet -.input-group-btn:last-child { - @extend .input-group-append; -} - /* Bootstrap 4.1.2 introduced a new default vertical alignment which breaks our icons, so we need to reset the vertical alignment to the default value. See: diff --git a/app/assets/stylesheets/components/popover.scss b/app/assets/stylesheets/components/popover.scss index 2f4d30fe923..7d46b262a69 100644 --- a/app/assets/stylesheets/components/popover.scss +++ b/app/assets/stylesheets/components/popover.scss @@ -7,3 +7,10 @@ line-height: $gl-line-height; } } + +.mr-popover { + .text-secondary { + font-size: 12px; + line-height: 1.33; + } +} diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 216877a4461..ab9047c54e4 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -60,6 +60,7 @@ @import 'framework/memory_graph'; @import 'framework/responsive_tables'; @import 'framework/stacked_progress_bar'; +@import 'framework/sortable'; @import 'framework/ci_variable_list'; @import 'framework/feature_highlight'; @import 'framework/terms'; diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index af79a4d9392..37a729c7a63 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -61,6 +61,10 @@ border: 0; } + &.avatar-placeholder { + border: 0; + } + &:not([href]):hover { border-color: darken($gray-normal, 10%); } diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss index 91dbb2a6365..cbd390e7145 100644 --- a/app/assets/stylesheets/framework/blank.scss +++ b/app/assets/stylesheets/framework/blank.scss @@ -69,6 +69,7 @@ @include media-breakpoint-up(sm) { display: flex; + height: 100%; align-items: center; padding: 50px 30px; } @@ -99,3 +100,30 @@ } } } + +@include media-breakpoint-up(lg) { + .column-large { + flex: 2; + } + + .column-small { + flex: 1; + margin-bottom: 15px; + + .blank-state { + max-width: 400px; + flex-wrap: wrap; + margin-left: 15px; + } + + .blank-state-icon { + margin-bottom: 30px; + } + } +} + +@include media-breakpoint-down(xs) { + .blank-state-icon svg { + width: 315px; + } +} diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index a4af84f8d27..695ce014659 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -443,7 +443,8 @@ border-color: transparent; } - &.btn-secondary-hover-link { + &.btn-secondary-hover-link, + &.btn-default-hover-link { color: $gl-text-color-secondary; &:hover, diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 5efd484b51b..8fc08422d76 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -120,7 +120,7 @@ hr { text-overflow: ellipsis; white-space: nowrap; - > div, + > div:not(.block), .str-truncated { display: inline; } @@ -381,6 +381,7 @@ img.emoji { .prepend-left-5 { margin-left: 5px; } .prepend-left-8 { margin-left: 8px; } .prepend-left-10 { margin-left: 10px; } +.prepend-left-15 { margin-left: 15px; } .prepend-left-default { margin-left: $gl-padding; } .prepend-left-20 { margin-left: 20px; } .prepend-left-32 { margin-left: 32px; } @@ -388,6 +389,7 @@ img.emoji { .append-right-5 { margin-right: 5px; } .append-right-8 { margin-right: 8px; } .append-right-10 { margin-right: 10px; } +.append-right-15 { margin-right: 15px; } .append-right-default { margin-right: $gl-padding; } .append-right-20 { margin-right: 20px; } .prepend-right-32 { margin-right: 32px; } @@ -402,6 +404,8 @@ img.emoji { .prepend-bottom-32 { margin-bottom: 32px; } .inline { display: inline-block; } .center { text-align: center; } +.block { display: block; } +.flex { display: flex; } .vertical-align-middle { vertical-align: middle; } .vertical-align-sub { vertical-align: sub; } .flex-align-self-center { align-self: center; } @@ -411,6 +415,12 @@ img.emoji { .ws-normal { white-space: normal; } .overflow-auto { overflow: auto; } +.d-flex-center { + display: flex; + align-items: center; + justify-content: center; +} + /** COMMON SIZING CLASSES **/ .w-0 { width: 0; } diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 6108eaa1ad0..8d38310e8e6 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -25,10 +25,6 @@ } } - table { - @extend .table; - } - .file-title { position: relative; background-color: $gray-light; @@ -123,7 +119,7 @@ } } - &.wiki { + &.md { padding: $gl-padding; @include media-breakpoint-up(md) { diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index f5ed6621c55..1e025b3a67d 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -304,7 +304,9 @@ } } -.caret-down { +.caret-down, +.btn .caret-down { + top: 0; height: 11px; width: 11px; margin-left: 4px; diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss index 49b9b7014ae..3ab61cc5c47 100644 --- a/app/assets/stylesheets/framework/icons.scss +++ b/app/assets/stylesheets/framework/icons.scss @@ -31,6 +31,7 @@ } } +.ci-status-icon-preparing, .ci-status-icon-running { svg { fill: $blue-400; diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 1a74e06a75d..298610a0631 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -156,6 +156,12 @@ ul.content-list { margin-top: 3px; margin-bottom: 4px; + &.btn-ldap-override { + @include media-breakpoint-up(sm) { + margin-bottom: 0; + } + } + &.has-tooltip, &:last-child { margin-right: 0; diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index d6c4e68f68f..b2cc3e2428a 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -61,6 +61,10 @@ padding-top: 0; line-height: 19px; + &.btn.btn-sm { + padding: 2px 5px; + } + &:focus { margin-top: -10px; padding-top: 10px; @@ -131,30 +135,6 @@ width: 100%; } -.md:not(.use-csslab) { - &.md-preview-holder { - // Reset ul style types since we're nested inside a ul already - @include bulleted-list; - } - - // On diffs code should wrap nicely and not overflow - code { - white-space: pre-wrap; - word-break: keep-all; - } - - hr { - // Darken 'whitesmoke' a bit to make it more visible in note bodies - border-color: darken($gray-normal, 8%); - margin: 10px 0; - } - - - table:not(.js-syntax-highlight) { - @include markdown-table; - } -} - .toolbar-btn { float: left; padding: 0 7px; diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 955ae80cd58..18eb10c1f23 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -22,27 +22,6 @@ } /* - * Mixin for markdown tables - */ -@mixin markdown-table { - width: auto; - display: block; - overflow-x: auto; - border: 0; - border-color: $gl-gray-100; - - tr { - th { - border-bottom: solid 2px $gl-gray-100; - } - - td { - border-color: $gl-gray-100; - } - } -} - -/* * Base mixin for lists in GitLab */ @mixin basic-list { @@ -94,20 +73,6 @@ } } -@mixin bulleted-list { - > ul { - list-style-type: disc; - - ul { - list-style-type: circle; - - ul { - list-style-type: square; - } - } - } -} - @mixin webkit-prefix($property, $value) { #{'-webkit-' + $property}: $value; #{$property}: $value; diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss index 3a117106cff..cd3d6f8297e 100644 --- a/app/assets/stylesheets/framework/panels.scss +++ b/app/assets/stylesheets/framework/panels.scss @@ -7,7 +7,6 @@ margin-bottom: $gl-vert-padding; } - .card-header { padding: $gl-vert-padding $gl-padding; line-height: 36px; diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss index 19640ab5986..31297b9d20c 100644 --- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss +++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss @@ -181,6 +181,33 @@ margin: 0; width: 100%; } + + &.inline { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + + > .btn, + > .btn-container, + > .dropdown, + > input, + > form { + flex: 1 1 auto; + margin: 0 0 10px; + margin-left: $gl-padding-top; + width: auto; + + &:first-child { + margin-left: 0; + float: none; + } + } + + .btn-full { + flex: 1 1 100%; + margin-left: 0; + } + } } } diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index ac673eafdc7..81ccea1e01f 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -264,6 +264,16 @@ } } +.project-result { + .project-name { + font-weight: $gl-font-weight-bold; + } + + .project-path { + color: $gl-gray-400; + } +} + .user-result { min-height: 24px; display: flex; diff --git a/app/assets/stylesheets/framework/sortable.scss b/app/assets/stylesheets/framework/sortable.scss new file mode 100644 index 00000000000..8c070200135 --- /dev/null +++ b/app/assets/stylesheets/framework/sortable.scss @@ -0,0 +1,92 @@ +.sortable-container { + background-color: $gray-light; + + .flex-list { + padding: 5px; + margin-bottom: 0; + } +} + +.sortable-row { + .flex-row { + display: flex; + + &.issuable-info-container { + padding-right: 0; + } + } + + .sortable-link { + color: $black; + } +} + +.gl-sortable { + .header { + user-select: none; + + &:hover { + cursor: pointer; + background-color: $gray-100; + } + + &:focus { + outline: 1px solid $blue-300; + } + } +} + +.related-issues-list-item { + .card-body, + .issuable-info-container { + padding: $gl-padding-4 $gl-padding-4 $gl-padding-4 $gl-padding; + + .block-truncated { + padding: $gl-padding-8 0; + line-height: $gl-btn-line-height; + } + + @include media-breakpoint-down(md) { + padding-left: $gl-padding; + + .block-truncated { + flex-direction: column-reverse; + padding: $gl-padding-4 0; + + .text-secondary { + margin-top: $gl-padding-4; + } + + .issue-token-title-text { + display: block; + } + } + + .issue-item-remove-button { + align-self: baseline; + } + } + + @include media-breakpoint-only(md) { + .block-truncated .issue-token-title-text { + white-space: nowrap; + } + + .issue-item-remove-button { + align-self: center; + } + } + + @include media-breakpoint-down(sm) { + padding-left: $gl-padding-8; + + .block-truncated .issue-token-title-text { + white-space: normal; + } + } + } + + &.is-dragging { + padding: 0; + } +} diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss index 3d66136938f..e5edddec71e 100644 --- a/app/assets/stylesheets/framework/system_messages.scss +++ b/app/assets/stylesheets/framework/system_messages.scss @@ -12,7 +12,7 @@ p { @include str-truncated(100%); - margin-top: 0; + margin-top: -1px; margin-bottom: 0; } } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 55ce0d7004e..244b414d334 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -1,4 +1,8 @@ -@mixin md-typography { +/** + * Apply Markdown typography + * + */ +.md:not(.use-csslab) { color: $gl-text-color; word-wrap: break-word; @@ -6,8 +10,35 @@ text-align: initial; } + *:first-child { + margin-top: 0; + } + + > :last-child { + margin-bottom: 0; + } + + p { + color: $gl-text-color; + margin: 0 0 16px; + + > code { + font-weight: inherit; + } + + a:not(.no-attachment-icon) img { + // Remove bottom padding because + // <p> already has $gl-padding bottom + margin-bottom: 0; + } + } + a { color: $blue-600; + + > code { + color: $blue-600; + } } img:not(.emoji) { @@ -28,18 +59,12 @@ max-width: 100%; } - p a:not(.no-attachment-icon) img { - // Remove bottom padding because - // <p> already has $gl-padding bottom - margin-bottom: 0; - } - - *:first-child { - margin-top: 0; - } - - > :last-child { - margin-bottom: 0; + &:not(.md-file) img:not(.emoji) { + border: 1px solid $white-normal; + padding: 5px; + margin: 5px 0; + // Ensure that image does not exceed viewport + max-height: calc(100vh - 100px); } // Single code lines should wrap @@ -47,6 +72,7 @@ font-family: $monospace-font; white-space: pre-wrap; word-wrap: normal; + word-break: keep-all; } kbd { @@ -131,20 +157,34 @@ } } - p { - color: $gl-text-color; - margin: 0 0 16px; + hr { + // Darken 'whitesmoke' a bit to make it more visible in note bodies + border-color: darken($gray-normal, 8%); + margin: 10px 0; } - table:not(.js-syntax-highlight) { + table:not(.code) { @extend .table; @extend .table-bordered; margin: 16px 0; color: $gl-text-color; border: 0; + width: auto; + display: block; + overflow-x: auto; - th { - background: $label-gray-bg; + tbody { + background-color: $white-light; + } + + tr { + th { + border-bottom: solid 2px $gl-gray-200; + } + + td { + border-color: $gl-gray-200; + } } } @@ -173,14 +213,6 @@ } } - p > code { - font-weight: inherit; - } - - a > code { - color: $blue-600; - } - dd { margin-left: $gl-padding; } @@ -196,6 +228,18 @@ margin: 3px 28px 3px 0 !important; } + > ul { + list-style-type: disc; + + ul { + list-style-type: circle; + + ul { + list-style-type: square; + } + } + } + li { line-height: 1.6em; margin-left: 25px; @@ -239,11 +283,11 @@ &:hover::before { text-decoration: none; } - } - a.no-attachment-icon { - &::before { - display: none; + &.no-attachment-icon { + &::before { + display: none; + } } } @@ -362,28 +406,6 @@ code { } /** - * Apply Markdown typography - * - */ -.wiki:not(.use-csslab) { - @include md-typography; -} - -.md:not(.use-csslab) { - @include md-typography; - - &:not(.wiki) { - img:not(.emoji) { - border: 1px solid $white-normal; - padding: 5px; - margin: 5px 0; - // Ensure that image does not exceed viewport - max-height: calc(100vh - 100px); - } - } -} - -/** * Textareas intended for GFM * */ diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 0333b9445c5..5d4c84c494d 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -23,6 +23,7 @@ $darken-border-dashed-factor: 25%; $white-light: #fff; $white-normal: #f0f0f0; $white-dark: #eaeaea; +$white-transparent: rgba(255, 255, 255, 0.8); $gray-lightest: #fdfdfd; $gray-light: #fafafa; @@ -277,7 +278,7 @@ $general-hover-transition-duration: 100ms; $general-hover-transition-curve: linear; $highlight-changes-color: rgb(235, 255, 232); $performance-bar-height: 35px; -$system-header-height: 35px; +$system-header-height: 16px; $system-footer-height: $system-header-height; $flash-height: 52px; $context-header-height: 60px; @@ -288,6 +289,10 @@ $gl-line-height: 16px; $gl-line-height-24: 24px; $gl-line-height-14: 14px; +$system-header-height: 35px; +$issue-box-upcoming-bg: #8f8f8f; +$pages-group-name-color: #4c4e54; + /* * Common component specific colors */ @@ -410,7 +415,7 @@ $award-emoji-menu-shadow: rgba(0, 0, 0, 0.175); $award-emoji-positive-add-bg: #fed159; $award-emoji-positive-add-lines: #bb9c13; $award-emoji-width: 376px; -$award-emoji-width-xs: 300px; +$award-emoji-width-xs: 90%; /* * Search Box @@ -626,6 +631,18 @@ Animation Functions $dropdown-animation-timing: cubic-bezier(0.23, 1, 0.32, 1); /* +GitLab Plans +*/ +$gl-gold-plan: #d4af37; +$gl-silver-plan: #91a1ab; +$gl-bronze-plan: #cd7f32; + +/* +Cross-project Pipelines + */ +$linked-project-column-margin: 60px; + +/* Performance Bar */ $perf-bar-production: #222; @@ -649,6 +666,17 @@ $image-comment-cursor-left-offset: 12; $image-comment-cursor-top-offset: 12; /* +Add GitLab Slack Application +*/ +$add-to-slack-popup-max-width: 400px; +$add-to-slack-gif-max-width: 850px; +$add-to-slack-well-max-width: 750px; +$add-to-slack-logo-size: 100px; +$double-headed-arrow-width: 100px; +$double-headed-arrow-height: 25px; +$right-arrow-size: 16px; + +/* Popup */ $popup-triangle-size: 15px; @@ -683,3 +711,11 @@ $mr-version-controls-height: 56px; Compare Branches */ $compare-branches-sticky-header-height: 68px; + +/** + Bootstrap 4.2.0 introduced new icons for validating forms. + Our design system does not use those, so we are disabling them for now: + - Docs: https://getbootstrap.com/docs/4.3/components/forms/#server-side + - Issue: https://gitlab.com/gitlab-org/design.gitlab.com/issues/242 + */ +$enable-validation-icons: false; diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss index efcc437bd7f..fb4d3f23cd9 100644 --- a/app/assets/stylesheets/framework/variables_overrides.scss +++ b/app/assets/stylesheets/framework/variables_overrides.scss @@ -9,7 +9,6 @@ $input-border-color: $gray-200; $input-color: $gl-text-color; $font-family-sans-serif: $regular-font; $font-family-monospace: $monospace-font; -$input-line-height: 20px; $btn-line-height: 20px; $table-accent-bg: $gray-light; $card-border-color: $border-color; diff --git a/app/assets/stylesheets/framework/vue_transitions.scss b/app/assets/stylesheets/framework/vue_transitions.scss index e07a177e153..e3bdc0b0199 100644 --- a/app/assets/stylesheets/framework/vue_transitions.scss +++ b/app/assets/stylesheets/framework/vue_transitions.scss @@ -1,9 +1,13 @@ .fade-enter-active, -.fade-leave-active { +.fade-leave-active, +.fade-in-enter-active, +.fade-out-leave-active { transition: opacity $sidebar-transition-duration $general-hover-transition-curve; } .fade-enter, +.fade-in-enter, +.fade-out-leave-to, .fade-leave-to { opacity: 0; } diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 161943766d4..434cbd6d21c 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -12,6 +12,10 @@ border-bottom: 1px solid $well-inner-border; } + &.borderless { + border-bottom: 0; + } + &.branch-info { .commit-sha, .commit-info { diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss index f24c80bd81c..d77b7dfad68 100644 --- a/app/assets/stylesheets/notify.scss +++ b/app/assets/stylesheets/notify.scss @@ -1,4 +1,4 @@ -@import "framework/variables"; +@import 'framework/variables'; img { max-width: 100%; diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 81216b2b98e..ed0e9db035b 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -19,6 +19,7 @@ .is-ghost { opacity: 0.3; + pointer-events: none; } .dropdown-projects { @@ -50,6 +51,19 @@ .content-wrapper { padding-bottom: 0; } + + .issues-details-filters { + display: flex; + } + + .filter-form { + width: 100%; + } +} + +.board-extra-actions { + font-size: 0; + white-space: nowrap; } .boards-app { @@ -236,7 +250,8 @@ } } -.board-blank-state { +.board-blank-state, +.board-promotion-state { padding: $gl-padding; background-color: $white-light; flex: 1; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index fa5a182243c..916f6cd3137 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -50,7 +50,6 @@ position: relative; } - .build-trace { @include build-trace(); } @@ -392,3 +391,14 @@ right: 0; margin-top: -17px; } + +@include media-breakpoint-down(sm) { + .top-bar { + .truncated-info { + white-space: nowrap; + overflow: hidden; + max-width: 220px; + text-overflow: ellipsis; + } + } +} diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index c88922ae5ea..1fedbd8bddb 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -89,138 +89,6 @@ } } - .note-text { - table { - font-family: $font-family-sans-serif; - } - } - - table { - width: 100%; - font-family: $monospace-font; - border: 0; - border-collapse: separate; - margin: 0; - padding: 0; - table-layout: fixed; - border-radius: 0 0 $border-radius-default $border-radius-default; - - .diff-line-num { - width: 50px; - position: relative; - - a { - transition: none; - } - } - - .line_holder td { - line-height: $code-line-height; - font-size: $code-font-size; - vertical-align: top; - - &.noteable_line { - position: relative; - } - - span { - white-space: pre-wrap; - - &.context-cell { - display: inline-block; - width: 100%; - height: 100%; - } - } - - .line { - word-wrap: break-word; - } - } - - &.left-side-selected { - td.line_content.parallel.right-side { - user-select: none; - } - } - - &.right-side-selected { - td.line_content.parallel.left-side { - user-select: none; - } - } - } - - tr.line_holder.parallel { - td.line_content.parallel { - width: 46%; - } - - .add-diff-note { - margin-left: -55px; - } - } - - .old_line, - .new_line { - user-select: none; - margin: 0; - border: 0; - padding: 0 5px; - border-right: 1px solid; - text-align: right; - min-width: 35px; - max-width: 50px; - width: 35px; - - a { - float: left; - width: 35px; - font-weight: $gl-font-weight-normal; - - &[disabled] { - cursor: default; - - &:hover, - &:active { - text-decoration: none; - } - } - } - } - - .line_content { - display: block; - margin: 0; - padding: 0 1.5em; - border: 0; - position: relative; - - &.parallel { - display: table-cell; - - span { - word-break: break-all; - } - } - - &.old { - &::before { - content: '-'; - position: absolute; - left: 0.5em; - } - } - - &.new { - &::before { - content: '+'; - position: absolute; - left: 0.5em; - } - } - } - .diff-loading-error-block { padding: $gl-padding * 2 $gl-padding; text-align: center; @@ -443,10 +311,6 @@ } } - .line_content { - white-space: pre-wrap; - } - .diff-file-container { .frame.deleted { border: 1px solid $deleted; @@ -508,6 +372,114 @@ } } +table.code { + width: 100%; + font-family: $monospace-font; + border: 0; + border-collapse: separate; + margin: 0; + padding: 0; + table-layout: fixed; + border-radius: 0 0 $border-radius-default $border-radius-default; + + tr.line_holder td { + line-height: $code-line-height; + font-size: $code-font-size; + vertical-align: top; + + span { + white-space: pre-wrap; + + &.context-cell { + display: inline-block; + width: 100%; + height: 100%; + } + + &.line { + word-wrap: break-word; + } + } + + &.diff-line-num { + user-select: none; + margin: 0; + border: 0; + padding: 0 10px 0 5px; + border-right: 1px solid; + text-align: right; + width: 50px; + position: relative; + + a { + transition: none; + float: left; + width: 100%; + font-weight: $gl-font-weight-normal; + + &[disabled] { + cursor: default; + + &:hover, + &:active { + text-decoration: none; + } + } + } + + &:not(.js-unfold-bottom) a::before { + content: attr(data-linenumber); + } + } + + &.line_content { + display: block; + margin: 0; + padding: 0 1.5em; + border: 0; + position: relative; + white-space: pre-wrap; + + &.parallel { + display: table-cell; + width: 46%; + + span { + word-break: break-all; + } + } + + &.old { + &::before { + content: '-'; + position: absolute; + left: 0.5em; + } + } + + &.new { + &::before { + content: '+'; + position: absolute; + left: 0.5em; + } + } + } + } + + &.left-side-selected { + td.line_content.parallel.right-side { + user-select: none; + } + } + + &.right-side-selected { + td.line_content.parallel.left-side { + user-select: none; + } + } +} + .diff-stats { align-items: center; padding: 0 0.25rem; @@ -608,16 +580,6 @@ } } -.file-holder { - .diff-line-num:not(.js-unfold-bottom) { - a { - &::before { - content: attr(data-linenumber); - } - } - } -} - .diff-comment-avatar-holders { position: absolute; height: 19px; diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 61ecf133b02..0eb854ecf98 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -346,7 +346,9 @@ } > .popover-title, - > .popover-content { + > .popover-content, + > .popover-header, + > .popover-body { padding: 8px; font-size: 12px; white-space: nowrap; diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 8ade995525a..0a07747e0d4 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -15,6 +15,11 @@ word-wrap: nowrap; } +.content-list .group-name { + font-weight: $gl-font-weight-bold; + color: $pages-group-name-color; +} + .group-row { @include basic-list-stats; @@ -172,6 +177,50 @@ } } +.card { + .shared_runners_limit_under_quota { + color: $green-500; + } + + .shared_runners_limit_over_quota { + color: $red-500; + } +} + +.pipeline-quota { + border-top: 1px solid $table-border-color; + border-bottom: 1px solid $table-border-color; + margin: 0 0 $gl-padding; + + .row { + padding-top: 10px; + padding-bottom: 10px; + } + + .right { + text-align: right; + } + + .progress { + height: 6px; + width: 100%; + margin-bottom: 0; + margin-top: 4px; + } +} + +.user-settings-pipeline-quota { + margin-top: $gl-padding; + + .pipeline-quota { + border-top: 0; + } +} + +table.pipeline-project-metrics tr td { + padding: $gl-padding; +} + .mattermost-icon svg { width: 16px; height: 16px; diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss index 161d4dbfb22..7610c5cf6f3 100644 --- a/app/assets/stylesheets/pages/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -37,12 +37,4 @@ .documentation { padding: 7px; - - // Border around images in the help pages. - img:not(.emoji) { - border: 1px solid $white-normal; - padding: 5px; - margin: 5px; - max-height: calc(100vh - 100px); - } } diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss index 7f800367cad..20240835fda 100644 --- a/app/assets/stylesheets/pages/import.scss +++ b/app/assets/stylesheets/pages/import.scss @@ -49,3 +49,15 @@ .import-projects-loading-icon { margin-top: $gl-padding-32; } + +.btn-import { + .loading-icon { + display: none; + } + + &.is-loading { + .loading-icon { + display: inline-block; + } + } +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 623fa485ba6..6415d902ca6 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -72,14 +72,6 @@ height: $gl-padding * 2; } - // Border around images in issue and MR descriptions. - .description img:not(.emoji) { - border: 1px solid $white-normal; - padding: 5px; - max-height: calc(100vh - 100px); - max-width: 100%; - } - .emoji-block { padding: 10px 0; } @@ -264,6 +256,10 @@ .selectbox { display: none; + + &.show { + display: block; + } } .btn-clipboard:hover { @@ -317,6 +313,7 @@ } .no-value, + .btn-default-hover-link, .btn-secondary-hover-link { color: $gl-text-color-secondary; } @@ -600,7 +597,6 @@ margin: -7px; } - .user-list { display: flex; flex-wrap: wrap; @@ -724,6 +720,7 @@ .issuable-main-info { flex: 1 auto; margin-right: 10px; + min-width: 0; .issue-weight-icon { vertical-align: sub; @@ -785,6 +782,7 @@ @media(max-width: map-get($grid-breakpoints, lg)-1) { .task-status, .issuable-due-date, + .issuable-weight, .project-ref-path { display: none; } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 9f30495a7ef..c7d2369a6b8 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -145,6 +145,11 @@ ul.related-merge-requests > li { } } +.issues-footer { + padding-top: $gl-padding; + padding-bottom: 37px; +} + .issues-nav-controls { font-size: 0; diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 75d219320ef..6f98b4f7f13 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -34,7 +34,7 @@ .dropdown-new-label { .dropdown-content { - max-height: 136px; + max-height: initial; } } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index eb32beb0972..e0b84e0f92d 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -14,6 +14,12 @@ } .member { + &.is-overriden { + .btn-ldap-override { + display: none !important; + } + } + .list-item-name { @include media-breakpoint-up(sm) { float: left; @@ -122,6 +128,49 @@ outline: 0; } +.members-ldap { + align-self: center; + height: 100%; + margin-right: 10px; + margin-left: -49px; +} + +.alert-member-ldap { + background-color: $orange-50; + + @include media-breakpoint-up(sm) { + line-height: 40px; + } + + > p { + float: left; + margin-bottom: 10px; + color: $orange-600; + + @include media-breakpoint-up(sm) { + padding-left: 55px; + margin-bottom: 0; + } + } + + .controls { + width: 100%; + + @include media-breakpoint-up(sm) { + width: auto; + } + } +} + +.btn-ldap-override { + width: 100%; + + @include media-breakpoint-up(sm) { + margin-left: 10px; + width: auto; + } +} + .flex-project-members-panel { display: flex; flex-direction: row; diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 44556060c65..7f8b8ea8100 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -166,6 +166,7 @@ float: left; .accept-merge-request { + &.ci-preparing, &.ci-pending, &.ci-running { @include btn-blue; @@ -806,7 +807,7 @@ .merge-request-tabs-holder { top: $header-height; - z-index: 300; + z-index: 250; background-color: $white-light; border-bottom: 1px solid $border-color; diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 3ca8e943a3a..49608a3964f 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -235,6 +235,7 @@ $status-box-line-height: 26px; padding: 0; } + .popover-body, .popover-content { padding: 0; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index faf85e151e3..0c334e919de 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -44,6 +44,7 @@ $note-form-margin-left: 72px; border: 1px solid $border-color; border-radius: $border-radius-default; margin: $gl-padding 0; + overflow: auto; &.system-note, &.note-form { @@ -224,14 +225,7 @@ $note-form-margin-left: 72px; overflow-y: hidden; .note-text { - @include md-typography; - // Reset ul style types since we're nested inside a ul already - @include bulleted-list; word-wrap: break-word; - - table { - @include markdown-table; - } } } @@ -742,7 +736,7 @@ $note-form-margin-left: 72px; .add-diff-note { @include btn-comment-icon; opacity: 0; - margin-left: -55px; + margin-left: -50px; position: absolute; top: 50%; transform: translateY(-50%); diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 2b6319ddd4f..bb08440fda8 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -498,7 +498,8 @@ list-style: none; } - &:last-child { + // when downstream pipelines are present, the last stage isn't the last column + &:last-child:not(.has-downstream) { .build { // Remove right connecting horizontal line from first build in last stage &:first-child::after { @@ -515,7 +516,8 @@ } } - &:first-child { + // when upstream pipelines are present, the first stage isn't the first column + &:first-child:not(.has-upstream) { .build { // Remove left curved connectors from all builds in first stage &:not(:first-child)::before { @@ -793,6 +795,7 @@ @include mini-pipeline-graph-color($white, $orange-100, $orange-200, $orange-500, $orange-600, $orange-700); } + &.ci-status-icon-preparing, &.ci-status-icon-running { @include mini-pipeline-graph-color($white, $blue-100, $blue-200, $blue-500, $blue-600, $blue-700); } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index ab26259c007..87cef43b923 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -266,21 +266,6 @@ padding-top: 20px; } - .cover-controls { - position: static; - padding: 0 16px; - margin-bottom: 20px; - display: flex; - - .btn { - flex-grow: 1; - - &:first-child { - margin-left: 0; - } - } - } - .user-profile-nav { a { margin-right: 0; @@ -320,6 +305,7 @@ table.u2f-registrations { margin: 20px -5px 0; .bordered-box { + padding: 32px; border: 1px solid $blue-300; border-radius: $border-radius-default; background-color: $blue-50; @@ -453,8 +439,17 @@ table.u2f-registrations { } } + .form-group > label { + font-weight: $gl-font-weight-bold; + } + + .form-group > .form-text { + font-size: $gl-font-size; + } + .emoji-menu-toggle-button { @include emoji-menu-toggle-button; + padding: 6px 10px; .no-emoji-placeholder { position: relative; @@ -476,3 +471,41 @@ table.u2f-registrations { .help-block { color: $gl-text-color-secondary; } + +.gitlab-slack-gif { + width: 100%; + max-width: $add-to-slack-gif-max-width; +} + +.gitlab-slack-well { + background-color: $white-light; + box-shadow: none; + max-width: $add-to-slack-well-max-width; +} + +.gitlab-slack-logo { + width: $add-to-slack-logo-size; + height: $add-to-slack-logo-size; +} + +.gitlab-slack-popup { + width: 100%; + max-width: $add-to-slack-popup-max-width; +} + +.gitlab-slack-right-arrow svg { + fill: $white-dark; + width: $right-arrow-size; + height: $right-arrow-size; + vertical-align: text-bottom; +} + +.gitlab-slack-double-headed-arrow { + vertical-align: text-top; + + svg { + fill: $gray-darker; + width: $double-headed-arrow-width; + height: $double-headed-arrow-height; + } +} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 8e53876eb4f..bcb306d97d5 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -18,12 +18,9 @@ } .input-group { - display: flex; - .select2-container { display: unset; max-width: unset; - width: unset !important; flex-grow: 1; } diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 54126577f93..e4ed685bd1b 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -216,6 +216,31 @@ } } +.nested-settings { + padding-left: 20px; +} + +.input-btn-group { + display: flex; + + .input-large { + flex: 1; + } + + .btn { + margin-left: 10px; + } +} + +.settings-flex-row { + display: flex; + align-items: center; + + .float-right { + margin-left: auto; + } +} + .prometheus-metrics-monitoring { .card { .card-toggle { @@ -246,6 +271,27 @@ } } + .custom-monitored-metrics { + .card-title { + display: flex; + align-items: center; + + > .btn-success { + margin-left: auto; + } + } + + .custom-metric { + display: flex; + align-items: center; + } + + .custom-metric-link-bold { + font-weight: $gl-font-weight-bold; + text-decoration: none; + } + } + .loading-metrics, .empty-metrics { padding: 30px 10px; @@ -280,6 +326,12 @@ } } +.saml-settings.info-well { + .form-control[readonly] { + background: $white-light; + } +} + .modal-doorkeepr-auth { .modal-body { padding: $gl-padding; diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index f4d568d02ac..a59bb31bdcb 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -44,6 +44,7 @@ } &.ci-info, + &.ci-preparing, &.ci-running { @include status-color($blue-100, $blue-500, $blue-600); } diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 82e887aa62a..3260aed143e 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -179,9 +179,3 @@ ul.wiki-pages-list.content-list { } } } - -.wiki:not(.use-csslab) { - table { - @include markdown-table; - } -} diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index bb10928a037..9ed1600419d 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -1,21 +1,21 @@ -.wiki h1, -.wiki h2, -.wiki h3, -.wiki h4, -.wiki h5, -.wiki h6 { +.md h1, +.md h2, +.md h3, +.md h4, +.md h5, +.md h6 { margin-top: 17px; } -.wiki h1 { +.md h1 { font-size: 30px; } -.wiki h2 { +.md h2 { font-size: 22px; } -.wiki h3 { +.md h3 { font-size: 18px; font-weight: 600; } |