Merge branch 'master' into ide

* master: (177 commits)
  Add changelog
  Bump gitlab-shell version to 5.8.0 to fix Git for Windows 2.14
  Make contextual sidebar collapsible
  Fixed sidebar context header hover colors
  Use correct `Environment`-class within `Gitlab` namespace
  Remove gl.Activities from Commits page
  Move `let` calls inside the `describe` block using them
  Add `/assign me` alias support for assigning issuables to oneself
  GRPC::Unavailable (< GRPC::BadStatus) is wrapped in a CommandError
  Use `broken_storage` in the fs_shards_spec.
  Eager load project creators for project dashboards
  Memoize a user's personal projects count
  Remove redundant query from User#recent_push
  Improve checking if projects would be returned
  Change spelling of gitlab-shell
  Remove unused #tree-holder
  Add custom linter for inline JavaScript to haml_lint
  Rename user_can_admin? because it's more accurate
  Synchronous zanata community contribution translation
  Add Korean translation to i18n
  ...
This commit is contained in:
Mike Greiling 2017-08-07 15:20:09 -05:00
commit 7767ceef47
393 changed files with 7787 additions and 5902 deletions

View File

@ -66,16 +66,18 @@ stages:
- mysql:latest
- redis:alpine
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
.only-if-want-mysql: &only-if-want-mysql
only:
- /mysql/
- /-stable/
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
- tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
- tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee
- //@gitlab/gitlab-ee
- tags@gitlab/gitlab-ee
# Skip all jobs except the ones that begin with 'docs/'.
# Used for commits including ONLY documentation changes.
@ -114,7 +116,7 @@ stages:
.rspec-knapsack-mysql: &rspec-knapsack-mysql
<<: *rspec-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
<<: *only-if-want-mysql
<<: *except-docs
.spinach-knapsack: &spinach-knapsack
@ -146,7 +148,7 @@ stages:
.spinach-knapsack-mysql: &spinach-knapsack-mysql
<<: *spinach-knapsack
<<: *use-mysql
<<: *only-master-and-ee-or-mysql
<<: *only-if-want-mysql
<<: *except-docs
.only-canonical-masters: &only-canonical-masters

View File

@ -35,9 +35,21 @@ linters:
HtmlAttributes:
enabled: true
IdNames:
enabled: false
ImplicitDiv:
enabled: true
InlineJavaScript:
enabled: true
InlineStyles:
enabled: false
InstanceVariables:
enabled: false
LeadingCommentSpace:
enabled: false
@ -54,6 +66,9 @@ linters:
ObjectReferenceAttributes:
enabled: true
RepeatedId:
enabled: false
RuboCop:
enabled: false
# These cops are incredibly noisy when it comes to HAML templates, so we
@ -101,3 +116,6 @@ linters:
UnnecessaryStringOutput:
enabled: true
ViewLength:
enabled: false

View File

@ -1708,8 +1708,6 @@ entry.
## 8.16.7 (2017-02-27)
- No changes.
- No changes.
- Fix MR changes tab size count when there are over 100 files in the diff.
## 8.16.6 (2017-02-17)
@ -1923,6 +1921,14 @@ entry.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support.
## 8.15.5 (2017-01-20)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.15.4 (2017-01-09)
- Make successful pipeline emails off for watchers. !8176
@ -2205,6 +2211,14 @@ entry.
- Speed up group milestone index by passing group_id to IssuesFinder. !8363
- Ensure issuable state changes only fire webhooks once.
## 8.14.7 (2017-01-21)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.14.6 (2017-01-10)
- Update the gitlab-markup gem to the version 1.5.1. !8509
@ -2487,6 +2501,14 @@ entry.
- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
- Fix 404 when visit /projects page
## 8.13.12 (2017-01-21)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.13.11 (2017-01-10)
- Update the gitlab-markup gem to the version 1.5.1. !8509

View File

@ -1 +1 @@
0.26.0
0.27.0

View File

@ -1 +1 @@
5.4.0
5.8.0

View File

@ -314,11 +314,11 @@ group :development, :test do
gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0'
gem 'fuubar', '~> 2.2.0'
gem 'database_cleaner', '~> 1.5.0'
gem 'factory_girl_rails', '~> 4.7.0'
gem 'rspec-rails', '~> 3.5.0'
gem 'rspec-rails', '~> 3.6.0'
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
@ -342,7 +342,7 @@ group :development, :test do
gem 'rubocop', '~> 0.49.1', require: false
gem 'rubocop-rspec', '~> 1.15.1', require: false
gem 'scss_lint', '~> 0.54.0', require: false
gem 'haml_lint', '~> 0.21.0', require: false
gem 'haml_lint', '~> 0.26.0', require: false
gem 'simplecov', '~> 0.14.0', require: false
gem 'flay', '~> 2.8.0', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
@ -391,7 +391,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client
gem 'gitaly', '~> 0.21.0'
gem 'gitaly', '~> 0.24.0'
gem 'toml-rb', '~> 0.3.15', require: false

View File

@ -156,7 +156,7 @@ GEM
devise (~> 4.0)
railties
rotp (~> 2.0)
diff-lcs (1.2.5)
diff-lcs (1.3)
diffy (3.1.0)
docile (1.1.5)
domain_name (0.5.20161021)
@ -250,8 +250,8 @@ GEM
foreman (0.78.0)
thor (~> 0.19.1)
formatador (0.2.5)
fuubar (2.0.0)
rspec (~> 3.0)
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21)
@ -269,7 +269,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
gitaly (0.21.0)
gitaly (0.24.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@ -356,10 +356,11 @@ GEM
googleauth (~> 0.5.1)
haml (4.0.7)
tilt
haml_lint (0.21.0)
haml (~> 4.0)
haml_lint (0.26.0)
haml (>= 4.0, < 5.1)
rainbow
rake (>= 10, < 13)
rubocop (>= 0.47.0)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
hamlit (2.6.1)
temple (~> 0.7.6)
@ -393,7 +394,7 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.2)
i18n (0.8.1)
i18n (0.8.6)
ice_nine (0.11.2)
influxdb (0.2.3)
cause
@ -610,7 +611,7 @@ GEM
pry-rails (0.3.5)
pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3)
rack (1.6.5)
rack (1.6.8)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.4.1)
@ -658,7 +659,7 @@ GEM
rainbow (2.2.2)
rake
raindrops (0.18.0)
rake (10.5.0)
rake (12.0.0)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
rdoc (4.2.2)
@ -702,30 +703,26 @@ GEM
chunky_png
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.0)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
rspec-core (3.6.0)
rspec-support (~> 3.6.0)
rspec-expectations (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
rspec-support (~> 3.6.0)
rspec-mocks (3.6.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-rails (3.5.0)
rspec-support (~> 3.6.0)
rspec-rails (3.6.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0)
rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-support (~> 3.6.0)
rspec-retry (0.4.5)
rspec-core
rspec-set (0.1.3)
rspec-support (3.5.0)
rspec-support (3.6.0)
rspec_profiling (0.0.5)
activerecord
pg
@ -860,7 +857,7 @@ GEM
truncato (0.7.8)
htmlentities (~> 4.3.1)
nokogiri (~> 1.6.1)
tzinfo (1.2.2)
tzinfo (1.2.3)
thread_safe (~> 0.1)
u2f (0.2.1)
uglifier (2.7.2)
@ -972,13 +969,13 @@ DEPENDENCIES
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
foreman (~> 0.78.0)
fuubar (~> 2.0.0)
fuubar (~> 2.2.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.0)
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.21.0)
gitaly (~> 0.24.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1)
@ -991,7 +988,7 @@ DEPENDENCIES
grape (~> 0.19.2)
grape-entity (~> 0.6.0)
grape-route-helpers (~> 2.0.0)
haml_lint (~> 0.21.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
hashie-forbidden_attributes
health_check (~> 2.6.0)
@ -1076,7 +1073,7 @@ DEPENDENCIES
responders (~> 2.0)
rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-rails (~> 3.6.0)
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
@ -1130,4 +1127,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.15.1
1.15.3

View File

@ -10,7 +10,7 @@ class AjaxLoadingSpinner {
e.target.setAttribute('disabled', '');
const iconElement = e.target.querySelector('i');
// get first fa- icon
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first();
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g)[0];
iconElement.dataset.icon = originalIcon;
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
$(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);

View File

@ -1,6 +1,6 @@
/* eslint-disable class-methods-use-this */
/* global Flash */
import _ from 'underscore';
import Cookies from 'js-cookie';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';

View File

@ -1,3 +1,4 @@
import _ from 'underscore';
import '../commons/bootstrap';
// Requires Input behavior
@ -48,7 +49,9 @@ function hideOrShowHelpBlock(form) {
$(() => {
const $form = $('form.js-requires-input');
$form.requiresInput();
hideOrShowHelpBlock($form);
$('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form));
if ($form) {
$form.requiresInput();
hideOrShowHelpBlock($form);
$('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form));
}
});

View File

@ -1,4 +1,3 @@
// Toggle button. Show/hide content inside parent container.
// Button does not change visibility. If button has icon - it changes chevron style.
//

View File

@ -2,6 +2,7 @@
/* global BoardService */
/* global Flash */
import _ from 'underscore';
import Vue from 'vue';
import VueResource from 'vue-resource';
import FilteredSearchBoards from './filtered_search_boards';

View File

@ -1,5 +1,6 @@
/* global ListLabel */
import _ from 'underscore';
import Cookies from 'js-cookie';
const Store = gl.issueBoards.BoardsStore;

View File

@ -1,8 +1,8 @@
/* global ListIssue */
import Vue from 'vue';
import queryData from '../../utils/query_data';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import queryData from '~/boards/utils/query_data';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import './header';
import './list';
import './footer';

View File

@ -1,5 +1,6 @@
/* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var,
promise/catch-or-return */
import _ from 'underscore';
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};

View File

@ -1,6 +1,6 @@
/* eslint-disable comma-dangle, space-before-function-paren, one-var, no-shadow, dot-notation, max-len */
/* global List */
import _ from 'underscore';
import Cookies from 'js-cookie';
window.gl = window.gl || {};

View File

@ -9,6 +9,7 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/tab';
import 'bootstrap-sass/assets/javascripts/bootstrap/transition';
import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip';
import 'bootstrap-sass/assets/javascripts/bootstrap/popover';
import 'bootstrap-sass/assets/javascripts/bootstrap/button';
// custom jQuery functions
$.fn.extend({

View File

@ -1,3 +1,4 @@
import 'underscore';
import './polyfills';
import './jquery';
import './bootstrap';

View File

@ -1,5 +1,5 @@
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
import _ from 'underscore';
import './lib/utils/common_utils';
import { placeholderImage } from './lazy_loader';

View File

@ -92,7 +92,7 @@ $(() => {
});
},
selectDefaultStage() {
const stage = this.state.stages.first();
const stage = this.state.stages[0];
this.selectStage(stage);
},
selectStage(stage) {

View File

@ -94,7 +94,7 @@ const JumpToDiscussion = Vue.extend({
hasDiscussionsToJumpTo = false;
}
}
} else if (activeTab !== 'notes') {
} else if (activeTab !== 'show') {
// If we are on the commits or builds tabs,
// there are no discussions to jump to.
hasDiscussionsToJumpTo = false;
@ -103,12 +103,12 @@ const JumpToDiscussion = Vue.extend({
if (!hasDiscussionsToJumpTo) {
// If there are no discussions to jump to on the current page,
// switch to the notes tab and jump to the first disucssion there.
window.mrTabs.activateTab('notes');
activeTab = 'notes';
window.mrTabs.activateTab('show');
activeTab = 'show';
jumpToFirstDiscussion = true;
}
if (activeTab === 'notes') {
if (activeTab === 'show') {
discussionsSelector = '.discussion[data-discussion-id]';
discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
}
@ -156,7 +156,7 @@ const JumpToDiscussion = Vue.extend({
let $target = $(`${discussionsSelector}[data-discussion-id="${nextUnresolvedDiscussionId}"]`);
if (activeTab === 'notes') {
if (activeTab === 'show') {
$target = $target.closest('.note-discussion');
// If the next discussion is closed, toggle it open.

View File

@ -80,10 +80,6 @@ import UserFeatureHelper from './helpers/user_feature_helper';
(function() {
var Dispatcher;
$(function() {
return new Dispatcher();
});
Dispatcher = (function() {
function Dispatcher() {
this.initSearch();
@ -338,7 +334,6 @@ import UserFeatureHelper from './helpers/user_feature_helper';
break;
case 'projects:commits:show':
CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit);
new gl.Activities();
shortcut_handler = new ShortcutsNavigation();
GpgBadges.fetch();
break;
@ -351,6 +346,8 @@ import UserFeatureHelper from './helpers/user_feature_helper';
break;
case 'projects:edit':
setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
break;
case 'projects:imports:show':
new ProjectImport();
@ -511,7 +508,7 @@ import UserFeatureHelper from './helpers/user_feature_helper';
new gl.DueDateSelectors();
break;
}
switch (path.first()) {
switch (path[0]) {
case 'sessions':
case 'omniauth_callbacks':
if (!gon.u2f) break;
@ -640,4 +637,8 @@ import UserFeatureHelper from './helpers/user_feature_helper';
return Dispatcher;
})();
$(function() {
new Dispatcher();
});
}).call(window);

View File

@ -1,11 +1,10 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, prefer-arrow-callback */
/* global Dropzone */
import _ from 'underscore';
import './preview_markdown';
window.DropzoneInput = (function() {
function DropzoneInput(form) {
Dropzone.autoDiscover = false;
const divHover = '<div class="div-dropzone-hover"></div>';
const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
const $attachButton = form.find('.button-attach-file');
@ -218,7 +217,7 @@ window.DropzoneInput = (function() {
value = e.clipboardData.getData('text/plain');
}
value = value.split("\r");
return value.first();
return value[0];
};
const showSpinner = function(e) {

View File

@ -1,3 +1,4 @@
import _ from 'underscore';
import emojiMap from 'emojis/digests.json';
import emojiAliases from 'emojis/aliases.json';

View File

@ -1,11 +0,0 @@
// TODO: remove this
// eslint-disable-next-line no-extend-native
Array.prototype.first = function first() {
return this[0];
};
// eslint-disable-next-line no-extend-native
Array.prototype.last = function last() {
return this[this.length - 1];
};

View File

@ -1,3 +1,5 @@
import _ from 'underscore';
/**
* Makes search request for content when user types a value in the search input.
* Updates the html content of the page with the received one.

View File

@ -1,3 +1,4 @@
import _ from 'underscore';
import FilteredSearchContainer from './container';
class DropdownUtils {
@ -122,11 +123,11 @@ class DropdownUtils {
if (!allowMultiple && itemInExistingTokens) {
updatedItem.droplab_hidden = true;
} else if (!lastKey || searchInput.split('').last() === ' ') {
} else if (!lastKey || _.last(searchInput.split('')) === ' ') {
updatedItem.droplab_hidden = false;
} else if (lastKey) {
const split = lastKey.split(':');
const tokenName = split[0].split(' ').last();
const tokenName = _.last(split[0].split(' '));
const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1;
updatedItem.droplab_hidden = tokenName ? match : false;

View File

@ -167,7 +167,7 @@ class FilteredSearchDropdownManager {
// Eg. token = 'label:'
const split = lastToken.split(':');
const dropdownName = split[0].split(' ').last();
const dropdownName = _.last(split[0].split(' '));
this.loadDropdown(split.length > 1 ? dropdownName : '');
} else if (lastToken) {
// Token has been initialized into an object because it has a value

View File

@ -367,7 +367,7 @@ class FilteredSearchManager {
const fragments = searchToken.split(':');
if (fragments.length > 1) {
const inputValues = fragments[0].split(' ');
const tokenKey = inputValues.last();
const tokenKey = _.last(inputValues);
if (inputValues.length > 1) {
inputValues.pop();

View File

@ -23,6 +23,7 @@ export const showSubLevelItems = (el) => {
const top = calculateTop(boundingRect, subItems.offsetHeight);
const isAbove = top < boundingRect.top;
subItems.classList.add('fly-out-list');
subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`;
if (isAbove) {

View File

@ -1,3 +1,4 @@
import _ from 'underscore';
import glRegexp from './lib/utils/regexp';
import AjaxCache from './lib/utils/ajax_cache';

View File

@ -1,5 +1,6 @@
/* eslint-disable func-names, no-underscore-dangle, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, prefer-rest-params, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func, no-mixed-operators */
/* global fuzzaldrinPlus */
import _ from 'underscore';
import { isObject } from './lib/utils/type_utility';
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput;
@ -158,7 +159,7 @@ GitLabDropdownFilter = (function() {
} else {
elements = this.options.elements();
if (search_text) {
return elements.each(function() {
elements.each(function() {
var $el, matches;
$el = $(this);
matches = fuzzaldrinPlus.match($el.text().trim(), search_text);
@ -171,8 +172,10 @@ GitLabDropdownFilter = (function() {
}
});
} else {
return elements.show().removeClass('option-hidden');
elements.show().removeClass('option-hidden');
}
elements.parent().find('.dropdown-menu-empty-link').toggleClass('hidden', elements.is(':visible'));
}
};
@ -782,9 +785,15 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) {
if (this.options.filterable) {
this.dropdown.one('transitionend', () => {
const initialScrollTop = $(window).scrollTop();
if (this.dropdown.is('.open')) {
this.filterInput.focus();
}
if ($(window).scrollTop() < initialScrollTop) {
$(window).scrollTop(initialScrollTop);
}
});
if (triggerFocus) {

View File

@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
import _ from 'underscore';
import d3 from 'd3';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';

View File

@ -1,5 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
import _ from 'underscore';
import d3 from 'd3';
const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

View File

@ -1,4 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */
import _ from 'underscore';
export default {
parse_log: function(log) {

View File

@ -1,6 +1,7 @@
/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */
/* global IssuableIndex */
/* global Flash */
import _ from 'underscore';
export default {
init({ container, form, issues, prefixId } = {}) {

View File

@ -1,6 +1,6 @@
/* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */
/* global IssuableIndex */
import _ from 'underscore';
import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';

View File

@ -1,7 +1,7 @@
/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread */
/* global Issuable */
/* global ListLabel */
import _ from 'underscore';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import DropdownUtils from './filtered_search/dropdown_utils';

View File

@ -86,8 +86,9 @@
// This is required to handle non-unicode characters in hash
hash = decodeURIComponent(hash);
var fixedTabs = document.querySelector('.js-tabs-affix');
var fixedNav = document.querySelector('.navbar-gitlab');
const fixedTabs = document.querySelector('.js-tabs-affix');
const fixedDiffStats = document.querySelector('.js-diff-files-changed.is-stuck');
const fixedNav = document.querySelector('.navbar-gitlab');
var adjustment = 0;
if (fixedNav) adjustment -= fixedNav.offsetHeight;
@ -104,6 +105,11 @@
if (fixedTabs) {
adjustment -= fixedTabs.offsetHeight;
}
if (fixedDiffStats) {
adjustment -= fixedDiffStats.offsetHeight;
}
window.scrollBy(0, adjustment);
}
};

View File

@ -1,3 +1,5 @@
import _ from 'underscore';
(() => {
/*
* TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints,

View File

@ -0,0 +1,23 @@
export const isSticky = (el, scrollY, stickyTop) => {
const top = el.offsetTop - scrollY;
if (top === stickyTop) {
el.classList.add('is-stuck');
} else {
el.classList.remove('is-stuck');
}
};
export default (el) => {
if (!el) return;
const computedStyle = window.getComputedStyle(el);
if (!/sticky/.test(computedStyle.position)) return;
const stickyTop = parseInt(computedStyle.top, 10);
document.addEventListener('scroll', () => isSticky(el, window.scrollY, stickyTop), {
passive: true,
});
};

View File

@ -16,9 +16,6 @@ import 'mousetrap';
import 'mousetrap/plugins/pause/mousetrap-pause';
import 'vendor/fuzzaldrin-plus';
// extensions
import './extensions/array';
// expose common libraries as globals (TODO: remove these)
window.jQuery = jQuery;
window.$ = jQuery;
@ -36,9 +33,6 @@ import './shortcuts_find_file';
import './shortcuts_issuable';
import './shortcuts_network';
// behaviors
import './behaviors/';
// templates
import './templates/issuable_template_selector';
import './templates/issuable_template_selectors';
@ -56,6 +50,9 @@ import './lib/utils/pretty_time';
import './lib/utils/text_utility';
import './lib/utils/url_utility';
// behaviors
import './behaviors/';
// u2f
import './u2f/authenticate';
import './u2f/error';
@ -86,7 +83,6 @@ import './copy_as_gfm';
import './copy_to_clipboard';
import './create_label';
import './diff';
import './dispatcher';
import './dropzone_input';
import './due_date_select';
import './files_comment_button';
@ -150,9 +146,13 @@ import './subscription';
import './subscription_select';
import './syntax_highlight';
import './dispatcher';
// eslint-disable-next-line global-require, import/no-commonjs
if (process.env.NODE_ENV !== 'production') require('./test_utils/');
Dropzone.autoDiscover = false;
document.addEventListener('beforeunload', function () {
// Unbind scroll events
$(document).off('scroll');

View File

@ -7,6 +7,7 @@ import Cookies from 'js-cookie';
import './breakpoints';
import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
import stickyMonitor from './lib/utils/sticky';
/* eslint-disable max-len */
// MergeRequestTabs
@ -266,6 +267,10 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
const $container = $('#diffs');
$container.html(data.html);
this.initChangesDropdown();
stickyMonitor(document.querySelector('.js-diff-files-changed'));
if (typeof gl.diffNotesCompileComponents !== 'undefined') {
gl.diffNotesCompileComponents();
}
@ -314,6 +319,13 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
});
}
initChangesDropdown() {
$('.js-diff-stats-dropdown').glDropdown({
filterable: true,
remoteFilter: false,
});
}
// Show or hide the loading spinner
//
// status - Boolean, true to show, false to hide

View File

@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
/* global Issuable */
/* global ListMilestone */
import _ from 'underscore';
(function() {
this.MilestoneSelect = (function() {

View File

@ -1,23 +1,63 @@
import Cookies from 'js-cookie';
import _ from 'underscore';
/* global bp */
import './breakpoints';
export default class NewNavSidebar {
constructor() {
this.initDomElements();
this.render();
}
initDomElements() {
this.$page = $('.page-with-sidebar');
this.$sidebar = $('.nav-sidebar');
this.$overlay = $('.mobile-overlay');
this.$openSidebar = $('.toggle-mobile-nav');
this.$closeSidebar = $('.close-nav-button');
this.$sidebarToggle = $('.js-toggle-sidebar');
}
bindEvents() {
this.$openSidebar.on('click', () => this.toggleSidebarNav(true));
this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
this.$overlay.on('click', () => this.toggleSidebarNav(false));
this.$sidebarToggle.on('click', () => {
const value = !this.$sidebar.hasClass('sidebar-icons-only');
this.toggleCollapsedSidebar(value);
});
$(window).on('resize', () => _.debounce(this.render(), 100));
}
static setCollapsedCookie(value) {
if (bp.getBreakpointSize() !== 'lg') {
return;
}
Cookies.set('sidebar_collapsed', value, { expires: 365 * 10 });
}
toggleSidebarNav(show) {
this.$sidebar.toggleClass('nav-sidebar-expanded', show);
this.$overlay.toggleClass('mobile-nav-open', show);
this.$sidebar.removeClass('sidebar-icons-only');
}
toggleCollapsedSidebar(collapsed) {
this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
this.$page.toggleClass('page-with-new-sidebar', !collapsed);
this.$page.toggleClass('page-with-icon-sidebar', collapsed);
NewNavSidebar.setCollapsedCookie(collapsed);
}
render() {
const breakpoint = bp.getBreakpointSize();
if (breakpoint === 'sm' || breakpoint === 'md') {
this.toggleCollapsedSidebar(true);
} else if (breakpoint === 'lg') {
const collapse = Cookies.get('sidebar_collapsed') === 'true';
this.toggleCollapsedSidebar(collapse);
}
}
}

View File

@ -11,6 +11,7 @@ newline-per-chained-call, no-useless-escape, class-methods-use-this */
/* global mrRefreshWidgetUrl */
import $ from 'jquery';
import _ from 'underscore';
import Cookies from 'js-cookie';
import autosize from 'vendor/autosize';
import Dropzone from 'dropzone';

View File

@ -9,8 +9,8 @@
</template>
<script>
import pdfjsLib from 'pdfjs-dist';
import workerSrc from 'vendor/pdf.worker';
import pdfjsLib from 'vendor/pdf';
import workerSrc from 'vendor/pdf.worker.min';
import page from './page/index.vue';

View File

@ -1,4 +1,6 @@
<script>
import _ from 'underscore';
export default {
props: {
initialCronInterval: {

View File

@ -1,7 +1,7 @@
<script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import '~/flash';
import stageColumnComponent from './stage_column_component.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import '../../../flash';
export default {
props: {

View File

@ -1,6 +1,7 @@
/* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */
import 'cropper';
import _ from 'underscore';
((global) => {
// Matches everything but the file name

View File

@ -10,14 +10,19 @@ import Cookies from 'js-cookie';
const $projectCloneField = $('#project_clone');
const $cloneBtnText = $('a.clone-dropdown-btn span');
const selectedCloneOption = $cloneBtnText.text().trim();
if (selectedCloneOption.length > 0) {
$(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
}
$('a', $cloneOptions).on('click', (e) => {
const $this = $(e.currentTarget);
const url = $this.attr('href');
e.preventDefault();
$('.active', $cloneOptions).not($this).removeClass('active');
$this.toggleClass('active');
$('.is-active', $cloneOptions).not($this).removeClass('is-active');
$this.toggleClass('is-active');
$projectCloneField.val(url);
$cloneBtnText.text($this.text());

View File

@ -1,6 +1,6 @@
export default function setupProjectEdit() {
const $transferForm = $('.js-project-transfer-form');
const $selectNamespace = $transferForm.find('.select2');
const $selectNamespace = $transferForm.find('select.select2');
$selectNamespace.on('change', () => {
$transferForm.find(':submit').prop('disabled', !$selectNamespace.val());

View File

@ -1,3 +1,5 @@
import _ from 'underscore';
export default class ProtectedBranchDropdown {
/**
* @param {Object} options containing

View File

@ -1,3 +1,5 @@
import _ from 'underscore';
export default class ProtectedTagDropdown {
/**
* @param {Object} options containing

View File

@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, max-len */
import _ from 'underscore';
import Cookies from 'js-cookie';
import SidebarHeightManager from './sidebar_height_manager';

View File

@ -3,6 +3,7 @@
/* global ShortcutsNavigation */
/* global sidebar */
import _ from 'underscore';
import 'mousetrap';
import './shortcuts_navigation';
@ -58,7 +59,7 @@ import './shortcuts_navigation';
});
// If replyField already has some content, add a newline before our quote
separator = replyField.val().trim() !== "" && "\n\n" || '';
replyField.val(function(_, current) {
replyField.val(function(a, current) {
return current + separator + quote.join('') + "\n";
});

View File

@ -1,3 +1,5 @@
import _ from 'underscore';
import '~/smart_interval';
import timeTracker from './time_tracker';

View File

@ -1,3 +1,5 @@
import _ from 'underscore';
export default {
init() {
if (!this.initialized) {
@ -30,4 +32,3 @@ export default {
}
},
};

View File

@ -52,6 +52,7 @@ export default class Todos {
}
updateRowStateClicked(e) {
e.stopPropagation();
e.preventDefault();
const target = e.target;
@ -92,6 +93,7 @@ export default class Todos {
}
updateAllStateClicked(e) {
e.stopPropagation();
e.preventDefault();
const target = e.currentTarget;
@ -142,6 +144,7 @@ export default class Todos {
if (gl.utils.isMetaClick(e)) {
const windowTarget = '_blank';
const selected = e.target;
e.stopPropagation();
e.preventDefault();
if (selected.tagName === 'IMG') {

View File

@ -3,6 +3,8 @@
/* global U2FError */
/* global U2FUtil */
import _ from 'underscore';
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> authenticated -> POST to server

View File

@ -3,6 +3,8 @@
/* global U2FError */
/* global U2FUtil */
import _ from 'underscore';
// Register U2F (universal 2nd factor) devices for users to authenticate with.
//
// State Flow #1: setup -> in_progress -> registered -> POST to server

View File

@ -1,5 +1,7 @@
/* eslint-disable comma-dangle, consistent-return, class-methods-use-this, arrow-parens, no-param-reassign, max-len */
import _ from 'underscore';
const debounceTimeoutDuration = 1000;
const invalidInputClass = 'gl-field-error-outline';
const successInputClass = 'gl-field-success-outline';

View File

@ -1,3 +1,4 @@
import _ from 'underscore';
import d3 from 'd3';
const LOADING_HTML = `
@ -6,6 +7,14 @@ const LOADING_HTML = `
</div>
`;
function getSystemDate(systemUtcOffsetSeconds) {
const date = new Date();
const localUtcOffsetMinutes = 0 - date.getTimezoneOffset();
const systemUtcOffsetMinutes = systemUtcOffsetSeconds / 60;
date.setMinutes((date.getMinutes() - localUtcOffsetMinutes) + systemUtcOffsetMinutes);
return date;
}
function formatTooltipText({ date, count }) {
const dateObject = new Date(date);
const dateDayName = gl.utils.getDayName(dateObject);
@ -21,7 +30,7 @@ function formatTooltipText({ date, count }) {
const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
export default class ActivityCalendar {
constructor(container, timestamps, calendarActivitiesPath) {
constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0) {
this.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = '';
@ -36,7 +45,7 @@ export default class ActivityCalendar {
this.timestampsTmp = [];
let group = 0;
const today = new Date();
const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0);
const oneYearAgo = new Date(today);

View File

@ -150,15 +150,21 @@ export default class UserTabs {
const $calendarWrap = this.$parentEl.find('.user-calendar');
const calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
let utcFormatted = 'UTC';
if (utcOffset !== 0) {
utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${(utcOffset / 3600)}`;
}
$.ajax({
dataType: 'json',
url: calendarPath,
success: (activityData) => {
$calendarWrap.html(CALENDAR_TEMPLATE);
$calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
// eslint-disable-next-line no-new
new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath);
new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath, utcOffset);
},
});

View File

@ -1,6 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
/* global Issuable */
/* global emitSidebarEvent */
import _ from 'underscore';
// TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = window.emitSidebarEvent || $.noop;

View File

@ -1,3 +1,5 @@
import tooltip from '../../vue_shared/directives/tooltip';
export default {
name: 'MRWidgetAuthor',
props: {
@ -5,11 +7,14 @@ export default {
showAuthorName: { type: Boolean, required: false, default: true },
showAuthorTooltip: { type: Boolean, required: false, default: false },
},
directives: {
tooltip,
},
template: `
<a
:href="author.webUrl || author.web_url"
class="author-link"
:class="{ 'has-tooltip': showAuthorTooltip }"
class="author-link inline"
:v-tooltip="showAuthorTooltip"
:title="author.name">
<img
:src="author.avatarUrl || author.avatar_url"

View File

@ -1,8 +1,8 @@
/* global Flash */
import '~/lib/utils/datetime_utility';
import { statusIconEntityMap } from '../../vue_shared/ci_status_icons';
import MemoryUsage from './mr_widget_memory_usage';
import StatusIcon from './mr_widget_status_icon';
import MRWidgetService from '../services/mr_widget_service';
export default {
@ -13,11 +13,7 @@ export default {
},
components: {
'mr-widget-memory-usage': MemoryUsage,
},
computed: {
svg() {
return statusIconEntityMap.icon_status_success;
},
'status-icon': StatusIcon,
},
methods: {
formatDate(date) {
@ -51,51 +47,51 @@ export default {
},
},
template: `
<div class="mr-widget-heading">
<div class="mr-widget-heading deploy-heading">
<div v-for="deployment in mr.deployments">
<div class="ci-widget">
<div class="ci-widget media">
<div class="ci-status-icon ci-status-icon-success">
<span class="js-icon-link icon-link">
<span class="ci-status-icon"
v-html="svg"
aria-hidden="true"></span>
<status-icon status="success" />
</span>
</div>
<span>
<span
v-if="hasDeploymentMeta(deployment)">
Deployed to
</span>
<a
v-if="hasDeploymentMeta(deployment)"
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
class="js-deploy-meta">
{{deployment.name}}
</a>
<span
v-if="hasExternalUrls(deployment)">
on
</span>
<a
v-if="hasExternalUrls(deployment)"
:href="deployment.external_url"
target="_blank"
rel="noopener noreferrer nofollow"
class="js-deploy-url">
<i
class="fa fa-external-link"
aria-hidden="true" />
{{deployment.external_url_formatted}}
</a>
<span
v-if="hasDeploymentTime(deployment)"
:data-title="deployment.deployed_at_formatted"
class="js-deploy-time"
data-toggle="tooltip"
data-placement="top">
{{formatDate(deployment.deployed_at)}}
<div class="media-body space-children">
<span>
<span
v-if="hasDeploymentMeta(deployment)">
Deployed to
</span>
<a
v-if="hasDeploymentMeta(deployment)"
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
class="js-deploy-meta inline">
{{deployment.name}}
</a>
<span
v-if="hasExternalUrls(deployment)">
on
</span>
<a
v-if="hasExternalUrls(deployment)"
:href="deployment.external_url"
target="_blank"
rel="noopener noreferrer nofollow"
class="js-deploy-url inline">
<i
class="fa fa-external-link"
aria-hidden="true" />
{{deployment.external_url_formatted}}
</a>
<span
v-if="hasDeploymentTime(deployment)"
:data-title="deployment.deployed_at_formatted"
class="js-deploy-time"
data-toggle="tooltip"
data-placement="top">
{{formatDate(deployment.deployed_at)}}
</span>
</span>
<button
type="button"
@ -104,13 +100,13 @@ export default {
class="btn btn-default btn-xs">
Stop environment
</button>
</span>
<mr-widget-memory-usage
v-if="deployment.metrics_url"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div>
</div>
<mr-widget-memory-usage
v-if="deployment.metrics_url"
:metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url"
/>
</div>
</div>
`,

View File

@ -1,3 +1,4 @@
import tooltip from '../../vue_shared/directives/tooltip';
import '../../lib/utils/text_utility';
export default {
@ -5,6 +6,9 @@ export default {
props: {
mr: { type: Object, required: true },
},
directives: {
tooltip,
},
computed: {
shouldShowCommitsBehindText() {
return this.mr.divergedCommitsCount > 0;
@ -29,18 +33,51 @@ export default {
},
template: `
<div class="mr-source-target">
<div
v-if="mr.isOpen"
class="pull-right">
<div class="normal">
<strong>
Request to merge
<span
class="label-branch"
:class="{'label-truncated': isBranchTitleLong(mr.sourceBranch)}"
:title="isBranchTitleLong(mr.sourceBranch) ? mr.sourceBranch : ''"
data-placement="bottom"
:v-tooltip="isBranchTitleLong(mr.sourceBranch)"
v-html="mr.sourceBranchLink"></span>
<button
v-tooltip
class="btn btn-transparent btn-clipboard"
data-title="Copy branch name to clipboard"
:data-clipboard-text="branchNameClipboardData">
<i
aria-hidden="true"
class="fa fa-clipboard"></i>
</button>
into
<span
class="label-branch"
:v-tooltip="isBranchTitleLong(mr.sourceBranch)"
:class="{'label-truncatedtooltip': isBranchTitleLong(mr.targetBranch)}"
:title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''"
data-placement="bottom">
<a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count">
(<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>)
</span>
</div>
<div v-if="mr.isOpen">
<a
href="#modal_merge_info"
data-toggle="modal"
class="btn inline btn-grouped btn-sm">
class="btn btn-small inline">
Check out branch
</a>
<span class="dropdown inline prepend-left-5">
<span class="dropdown inline prepend-left-10">
<a
class="btn btn-sm dropdown-toggle"
class="btn btn-xs dropdown-toggle"
data-toggle="dropdown"
aria-label="Download as"
role="button">
@ -69,38 +106,6 @@ export default {
</ul>
</span>
</div>
<div class="normal">
<strong>
Request to merge
<span
class="label-branch"
:class="{'label-truncated has-tooltip': isBranchTitleLong(mr.sourceBranch)}"
:title="isBranchTitleLong(mr.sourceBranch) ? mr.sourceBranch : ''"
data-placement="bottom"
v-html="mr.sourceBranchLink"></span>
<button
class="btn btn-transparent btn-clipboard has-tooltip"
data-title="Copy branch name to clipboard"
:data-clipboard-text="branchNameClipboardData">
<i
aria-hidden="true"
class="fa fa-clipboard"></i>
</button>
into
<span
class="label-branch"
:class="{'label-truncated has-tooltip': isBranchTitleLong(mr.targetBranch)}"
:title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''"
data-placement="bottom">
<a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count">
(<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>)
</span>
</div>
</div>
`,
};

View File

@ -120,13 +120,12 @@ export default {
},
template: `
<div class="mr-info-list clearfix mr-memory-usage js-mr-memory-usage">
<div class="legend"></div>
<p
v-if="shouldShowLoading"
class="usage-info js-usage-info usage-info-loading">
<i
class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true" />Loading deployment statistics.
aria-hidden="true" />Loading deployment statistics
</p>
<p
v-if="shouldShowMemoryGraph"
@ -136,12 +135,12 @@ export default {
<p
v-if="shouldShowLoadFailure"
class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics.
Failed to load deployment statistics
</p>
<p
v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently.
Deployment statistics are not available currently
</p>
<mr-memory-graph
v-if="shouldShowMemoryGraph"

View File

@ -16,7 +16,7 @@ export default {
<a
data-toggle="modal"
href="#modal_merge_info">
command line.
command line
</a>
</section>
`,

View File

@ -29,58 +29,55 @@ export default {
},
template: `
<div class="mr-widget-heading">
<div class="ci-widget">
<div class="ci-widget media">
<template v-if="hasCIError">
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error">
<span class="js-icon-link icon-link">
<span
v-html="svg"
aria-hidden="true"></span>
</span>
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
<span
v-html="svg"
aria-hidden="true"></span>
</div>
<div class="media-body">
Could not connect to the CI server. Please check your settings and try again
</div>
<span>Could not connect to the CI server. Please check your settings and try again.</span>
</template>
<template v-else>
<div>
<div class="ci-status-icon append-right-10">
<a
class="icon-link"
:href="this.status.details_path">
<ci-icon :status="status" />
</a>
</div>
<span>
Pipeline
<a
:href="mr.pipeline.path"
class="pipeline-id">#{{mr.pipeline.id}}</a>
{{mr.pipeline.details.status.label}}
</span>
<span
v-if="mr.pipeline.details.stages.length > 0">
with {{stageText}}
</span>
<div class="mr-widget-pipeline-graph">
<div class="stage-cell">
<div
v-if="mr.pipeline.details.stages.length > 0"
v-for="stage in mr.pipeline.details.stages"
class="stage-container dropdown js-mini-pipeline-graph">
<pipeline-stage :stage="stage" />
</div>
</div>
<div class="media-body">
<span>
Pipeline
<a
:href="mr.pipeline.path"
class="pipeline-id">#{{mr.pipeline.id}}</a>
</span>
<span class="mr-widget-pipeline-graph">
<span class="stage-cell">
<div
v-if="mr.pipeline.details.stages.length > 0"
v-for="stage in mr.pipeline.details.stages"
class="stage-container dropdown js-mini-pipeline-graph">
<pipeline-stage :stage="stage" />
</div>
</span>
</span>
<span>
{{mr.pipeline.details.status.label}} for
<a
:href="mr.pipeline.commit.commit_path"
class="commit-sha js-commit-link">
{{mr.pipeline.commit.short_id}}</a>.
</span>
<span
v-if="mr.pipeline.coverage"
class="js-mr-coverage">
Coverage {{mr.pipeline.coverage}}%
</span>
</div>
<span>
for
<a
:href="mr.pipeline.commit.commit_path"
class="commit-sha js-commit-link">
{{mr.pipeline.commit.short_id}}</a>.
</span>
<span
v-if="mr.pipeline.coverage"
class="js-mr-coverage">
Coverage {{mr.pipeline.coverage}}%.
</span>
</template>
</div>
</div>

View File

@ -2,37 +2,32 @@ export default {
name: 'MRWidgetRelatedLinks',
props: {
relatedLinks: { type: Object, required: true },
state: { type: String, required: false },
},
computed: {
hasLinks() {
const { closing, mentioned, assignToMe } = this.relatedLinks;
return closing || mentioned || assignToMe;
},
},
methods: {
hasMultipleIssues(text) {
return !text ? false : text.match(/<\/a> and <a/);
},
issueLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'issues' : 'issue';
},
verbLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'are' : 'is';
closesText() {
if (this.state === 'merged') {
return 'Closed';
}
if (this.state === 'closed') {
return 'Did not close';
}
return 'Closes';
},
},
template: `
<section
v-if="hasLinks"
class="mr-info-list mr-links">
<div class="legend"></div>
<p v-if="relatedLinks.closing">
Closes {{issueLabel('closing')}}
<span v-html="relatedLinks.closing"></span>.
{{closesText}} <span v-html="relatedLinks.closing"></span>
</p>
<p v-if="relatedLinks.mentioned">
<span class="capitalize">{{issueLabel('mentioned')}}</span>
<span v-html="relatedLinks.mentioned"></span>
{{verbLabel('mentioned')}} mentioned but will not be closed.
Mentions <span v-html="relatedLinks.mentioned"></span>
</p>
<p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span>

View File

@ -0,0 +1,36 @@
import ciIcon from '../../vue_shared/components/ci_icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
props: {
status: { type: String, required: true },
showDisabledButton: { type: Boolean, required: false },
},
components: {
ciIcon,
loadingIcon,
},
computed: {
statusObj() {
return {
group: this.status,
icon: `icon_status_${this.status}`,
};
},
},
template: `
<div class="space-children flex-container-block append-right-10">
<div v-if="status === 'loading'" class="mr-widget-icon">
<loading-icon />
</div>
<ci-icon v-else :status="statusObj" />
<button
v-if="showDisabledButton"
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
</div>
`,
};

View File

@ -1,16 +1,26 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetArchived',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold">
This project is archived, write access has been disabled.
</span>
<div class="mr-widget-body media">
<div class="space-children">
<status-icon status="failed" />
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
</div>
<div class="media-body">
<span class="bold">
This project is archived, write access has been disabled
</span>
</div>
</div>
`,
};

View File

@ -1,4 +1,5 @@
import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetAutoMergeFailed',
@ -10,6 +11,9 @@ export default {
isRefreshing: false,
};
},
components: {
statusIcon,
},
methods: {
refreshWidget() {
this.isRefreshing = true;
@ -19,18 +23,16 @@ export default {
},
},
template: `
<div class="mr-widget-body">
<button
class="btn btn-success btn-small"
disabled="true"
type="button">
Merge
</button>
<span class="bold danger">
This merge request failed to be merged automatically.
<div class="mr-widget-body media">
<status-icon status="failed" />
<div class="media-body space-children">
<span class="bold">
<template v-if="mr.mergeError">{{mr.mergeError}}.</template>
This merge request failed to be merged automatically
</span>
<button
@click="refreshWidget"
:class="{ disabled: isRefreshing }"
:disabled="isRefreshing"
type="button"
class="btn btn-xs btn-default">
<i
@ -39,9 +41,6 @@ export default {
aria-hidden="true" />
Refresh
</button>
</span>
<div class="merge-error-text danger bold">
{{mr.mergeError}}
</div>
</div>
`,

View File

@ -1,19 +1,18 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetChecking',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold">
Checking ability to merge automatically.
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</span>
<div class="mr-widget-body media">
<status-icon status="loading" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
Checking ability to merge automatically
</span>
</div>
</div>
`,
};

View File

@ -1,4 +1,5 @@
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetClosed',
@ -7,24 +8,28 @@ export default {
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
statusIcon,
},
template: `
<div class="mr-widget-body">
<mr-widget-author-and-time
actionText="Closed by"
:author="mr.closedBy"
:dateTitle="mr.updatedAt"
:dateReadable="mr.closedAt"
/>
<section>
<p>
The changes were not merged into
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}</a>.
</p>
</section>
<div class="mr-widget-body media">
<status-icon status="failed" />
<div class="media-body">
<mr-widget-author-and-time
actionText="Closed by"
:author="mr.closedBy"
:dateTitle="mr.updatedAt"
:dateReadable="mr.closedAt"
/>
<section class="mr-info-list">
<p>
The changes were not merged into
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}</a>
</p>
</section>
</div>
</div>
`,
};

View File

@ -1,27 +1,25 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetConflicts',
props: {
mr: { type: Object, required: true },
},
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold">
There are merge conflicts.
<span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally.
<div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
There are merge conflicts<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally
</span>
</span>
</span>
<div
v-if="mr.canMerge"
class="btn-group">
<a
v-if="mr.conflictResolutionPath"
v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath"
class="btn btn-default btn-xs js-resolve-conflicts-button">
Resolve conflicts

View File

@ -1,3 +1,4 @@
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub';
export default {
@ -38,39 +39,40 @@ export default {
}
},
},
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
class="btn btn-success btn-small"
disabled="true"
type="button">
Merge
</button>
<span
v-if="!isRefreshing"
class="bold danger">
<span
class="has-error-message"
v-if="mr.mergeError">
{{mr.mergeError}}
<div class="mr-widget-body media">
<template v-if="isRefreshing">
<status-icon status="loading" />
<span class="media-body bold js-refresh-label">
Refreshing now
</span>
<span v-else>Merge failed.</span>
<span
:class="{ 'has-custom-error': mr.mergeError }">
Refreshing in {{timerText}} to show the updated status...
</span>
<button
@click="refresh"
class="btn btn-default btn-xs js-refresh-button"
type="button">
Refresh now
</button>
</span>
<span
v-if="isRefreshing"
class="bold js-refresh-label">
Refreshing now...
</span>
</template>
<template v-else>
<status-icon status="failed" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
<span
class="has-error-message"
v-if="mr.mergeError">
{{mr.mergeError}}.
</span>
<span v-else>Merge failed.</span>
<span
:class="{ 'has-custom-error': mr.mergeError }">
Refreshing in {{timerText}} to show the updated status...
</span>
</span>
<button
@click="refresh"
class="btn btn-default btn-xs js-refresh-button"
type="button">
Refresh now
</button>
</div>
</template>
</div>
`,
};

View File

@ -1,24 +1,29 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetLocked',
props: {
mr: { type: Object, required: true },
},
components: {
statusIcon,
},
template: `
<div class="mr-widget-body mr-state-locked">
<span class="state-label">Locked</span>
This merge request is in the process of being merged, during which time it is locked and cannot be closed.
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
<section class="mr-info-list mr-links">
<div class="legend"></div>
<p>
The changes will be merged into
<span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>.
</p>
</section>
<div class="mr-widget-body mr-state-locked media">
<status-icon status="loading" />
<div class="media-body">
<h4>
This merge request is in the process of being merged, during which time it is locked and cannot be closed
</h4>
<section class="mr-info-list">
<p>
The changes will be merged into
<span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>
</p>
</section>
</div>
</div>
`,
};

View File

@ -1,5 +1,5 @@
/* global Flash */
import statusIcon from '../mr_widget_status_icon';
import MRWidgetAuthor from '../../components/mr_widget_author';
import eventHub from '../../event_hub';
@ -11,6 +11,7 @@ export default {
},
components: {
'mr-widget-author': MRWidgetAuthor,
statusIcon,
},
data() {
return {
@ -61,56 +62,56 @@ export default {
},
},
template: `
<div class="mr-widget-body">
<h4>
Set by
<mr-widget-author :author="mr.setToMWPSBy" />
to be merged automatically when the pipeline succeeds.
<a
v-if="mr.canCancelAutomaticMerge"
@click.prevent="cancelAutomaticMerge"
:disabled="isCancellingAutoMerge"
role="button"
href="#"
class="btn btn-xs btn-default js-cancel-auto-merge">
<i
v-if="isCancellingAutoMerge"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
Cancel automatic merge
</a>
</h4>
<section class="mr-info-list">
<div class="legend"></div>
<p>The changes will be merged into
<div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
<h4>
Set by
<mr-widget-author :author="mr.setToMWPSBy" />
to be merged automatically when the pipeline succeeds
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}
</a>.
</p>
<p v-if="mr.shouldRemoveSourceBranch">
The source branch will be removed.
</p>
<p
v-else
class="with-button">
The source branch will not be removed.
<a
v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch"
@click.prevent="removeSourceBranch"
v-if="mr.canCancelAutomaticMerge"
@click.prevent="cancelAutomaticMerge"
:disabled="isCancellingAutoMerge"
role="button"
class="btn btn-xs btn-default js-remove-source-branch"
href="#">
href="#"
class="btn btn-xs btn-default js-cancel-auto-merge">
<i
v-if="isRemovingSourceBranch"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
Remove source branch
v-if="isCancellingAutoMerge"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
Cancel automatic merge
</a>
</p>
</section>
</h4>
<section class="mr-info-list">
<p>The changes will be merged into
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}
</a>
</p>
<p v-if="mr.shouldRemoveSourceBranch">
The source branch will be removed
</p>
<p v-else>
The source branch will not be removed
<a
v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch"
@click.prevent="removeSourceBranch"
role="button"
class="btn btn-xs btn-default js-remove-source-branch"
href="#">
<i
v-if="isRemovingSourceBranch"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
Remove source branch
</a>
</p>
</section>
</div>
</div>
`,
};

View File

@ -1,6 +1,9 @@
/* global Flash */
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import tooltip from '../../../vue_shared/directives/tooltip';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub';
export default {
@ -9,14 +12,19 @@ export default {
mr: { type: Object, required: true },
service: { type: Object, required: true },
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
},
data() {
return {
isMakingRequest: false,
};
},
directives: {
tooltip,
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
loadingIcon,
statusIcon,
},
computed: {
shouldShowRemoveSourceBranch() {
const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
@ -55,75 +63,77 @@ export default {
},
},
template: `
<div class="mr-widget-body">
<mr-widget-author-and-time
actionText="Merged by"
:author="mr.mergedBy"
:dateTitle="mr.updatedAt"
:dateReadable="mr.mergedAt" />
<section class="mr-info-list">
<div class="legend"></div>
<p>
The changes were merged into
<span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>
</p>
<p v-if="mr.sourceBranchRemoved">The source branch has been removed.</p>
<p v-if="shouldShowRemoveSourceBranch">
You can remove source branch now.
<button
@click="removeSourceBranch"
:class="{ disabled: isMakingRequest }"
type="button"
class="btn btn-xs btn-default js-remove-branch-button">
Remove Source Branch
</button>
</p>
<p v-if="shouldShowSourceBranchRemoving">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
The source branch is being removed.
</p>
</section>
<div
v-if="shouldShowMergedButtons"
class="merged-buttons clearfix">
<a
v-if="mr.canRevertInCurrentMR"
class="btn btn-close btn-sm has-tooltip"
href="#modal-revert-commit"
data-toggle="modal"
data-container="body"
title="Revert this merge request in a new merge request">
Revert
</a>
<a
v-else-if="mr.revertInForkPath"
class="btn btn-close btn-sm has-tooltip"
data-method="post"
:href="mr.revertInForkPath"
title="Revert this merge request in a new merge request">
Revert
</a>
<a
v-if="mr.canCherryPickInCurrentMR"
class="btn btn-default btn-sm has-tooltip"
href="#modal-cherry-pick-commit"
data-toggle="modal"
data-container="body"
title="Cherry-pick this merge request in a new merge request">
Cherry-pick
</a>
<a
v-else-if="mr.cherryPickInForkPath"
class="btn btn-default btn-sm has-tooltip"
data-method="post"
:href="mr.cherryPickInForkPath"
title="Cherry-pick this merge request in a new merge request">
Cherry-pick
</a>
<div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
<div class="space-children">
<mr-widget-author-and-time
actionText="Merged by"
:author="mr.mergedBy"
:dateTitle="mr.updatedAt"
:dateReadable="mr.mergedAt" />
<a
v-if="mr.canRevertInCurrentMR"
v-tooltip
class="btn btn-close btn-xs"
href="#modal-revert-commit"
data-toggle="modal"
data-container="body"
title="Revert this merge request in a new merge request">
Revert
</a>
<a
v-else-if="mr.revertInForkPath"
v-tooltip
class="btn btn-close btn-xs"
data-method="post"
:href="mr.revertInForkPath"
title="Revert this merge request in a new merge request">
Revert
</a>
<a
v-if="mr.canCherryPickInCurrentMR"
v-tooltip
class="btn btn-default btn-xs"
href="#modal-cherry-pick-commit"
data-toggle="modal"
data-container="body"
title="Cherry-pick this merge request in a new merge request">
Cherry-pick
</a>
<a
v-else-if="mr.cherryPickInForkPath"
v-tooltip
class="btn btn-default btn-xs"
data-method="post"
:href="mr.cherryPickInForkPath"
title="Cherry-pick this merge request in a new merge request">
Cherry-pick
</a>
</div>
<section class="mr-info-list">
<p>
The changes were merged into
<span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>
</p>
<p v-if="mr.sourceBranchRemoved">The source branch has been removed</p>
<p v-if="shouldShowRemoveSourceBranch" class="space-children">
<span>You can remove source branch now</span>
<button
@click="removeSourceBranch"
:disabled="isMakingRequest"
type="button"
class="btn btn-xs btn-default js-remove-branch-button">
Remove Source Branch
</button>
</p>
<p v-if="shouldShowSourceBranchRemoving">
<loading-icon inline />
<span>The source branch is being removed</span>
</p>
</section>
</div>
</div>
`,

View File

@ -1,3 +1,5 @@
import statusIcon from '../mr_widget_status_icon';
import tooltip from '../../../vue_shared/directives/tooltip';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help';
export default {
@ -5,30 +7,37 @@ export default {
props: {
mr: { type: Object, required: true },
},
directives: {
tooltip,
},
components: {
'mr-widget-merge-help': mrWidgetMergeHelp,
statusIcon,
},
computed: {
missingBranchName() {
return this.mr.sourceBranchRemoved ? 'source' : 'target';
},
message() {
return `If the ${this.missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line`;
},
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold js-branch-text">
<span class="capitalize">
{{missingBranchName}}
</span> branch does not exist.
Please restore the {{missingBranchName}} branch or use a different {{missingBranchName}} branch.
</span>
<mr-widget-merge-help
:missing-branch="missingBranchName" />
<div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton />
<div class="media-body space-children">
<span class="bold js-branch-text">
<span class="capitalize">
{{missingBranchName}}
</span> branch does not exist.
Please restore it or use a different {{missingBranchName}} branch
<i
v-tooltip
class="fa fa-question-circle"
:title="message"
:aria-label="message"></i>
</span>
</div>
</div>
`,
};

View File

@ -1,17 +1,19 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetNotAllowed',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold">
Ready to be merged automatically.
Ask someone with write access to this repository to merge this request.
</span>
<div class="mr-widget-body media">
<status-icon status="success" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
Ready to be merged automatically.
Ask someone with write access to this repository to merge this request
</span>
</div>
</div>
`,
};

View File

@ -12,7 +12,7 @@ export default {
return { emptyStateSVG };
},
template: `
<div class="mr-widget-body empty-state">
<div class="mr-widget-body mr-widget-empty-state">
<div class="row">
<div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center">
<span v-html="emptyStateSVG"></span>
@ -29,12 +29,14 @@ export default {
Currently there are no changes in this merge request's source branch.
Please push new commits or use a different branch.
</p>
<a
v-if="mr.newBlobPath"
:href="mr.newBlobPath"
class="btn btn-inverted btn-save">
Create file
</a>
<div>
<a
v-if="mr.newBlobPath"
:href="mr.newBlobPath"
class="btn btn-inverted btn-save">
Create file
</a>
</div>
</div>
</div>
</div>

View File

@ -1,16 +1,18 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold">
Pipeline blocked. The pipeline for this merge request requires a manual action to proceed.
</span>
<div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
Pipeline blocked. The pipeline for this merge request requires a manual action to proceed
</span>
</div>
</div>
`,
};

View File

@ -1,16 +1,18 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
class="btn btn-success btn-small"
disabled="true"
type="button">
Merge
</button>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure.
</span>
<div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
</span>
</div>
</div>
`,
};

View File

@ -1,8 +1,8 @@
/* global Flash */
import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub';
export default {
@ -25,6 +25,9 @@ export default {
warningSvg,
};
},
components: {
statusIcon,
},
computed: {
commitMessageLinkTitle() {
const withDesc = 'Include description in commit message';
@ -196,84 +199,98 @@ export default {
},
},
template: `
<div class="mr-widget-body">
<span class="btn-group">
<button
@click="handleMergeButtonClick()"
:disabled="isMergeButtonDisabled"
:class="mergeButtonClass"
type="button">
<i
v-if="isMakingRequest"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
{{mergeButtonText}}
</button>
<button
v-if="shouldShowMergeOptionsDropdown"
:disabled="isMergeButtonDisabled"
type="button"
class="btn btn-small btn-info dropdown-toggle"
data-toggle="dropdown">
<i
class="fa fa-caret-down"
aria-hidden="true" />
<span class="sr-only">
Select merge moment
<div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
<div class="media space-children">
<span class="btn-group">
<button
@click="handleMergeButtonClick()"
:disabled="isMergeButtonDisabled"
:class="mergeButtonClass"
type="button">
<i
v-if="isMakingRequest"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
{{mergeButtonText}}
</button>
<button
v-if="shouldShowMergeOptionsDropdown"
:disabled="isMergeButtonDisabled"
type="button"
class="btn btn-small btn-info dropdown-toggle js-merge-moment"
data-toggle="dropdown"
aria-label="Select merge moment">
<i
class="fa fa-chevron-down"
aria-hidden="true" />
</button>
<ul
v-if="shouldShowMergeOptionsDropdown"
class="dropdown-menu dropdown-menu-right"
role="menu">
<li>
<a
@click.prevent="handleMergeButtonClick(true)"
class="merge_when_pipeline_succeeds"
href="#">
<span class="media">
<span
v-html="successSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="media-body merge-opt-title">Merge when pipeline succeeds</span>
</span>
</a>
</li>
<li>
<a
@click.prevent="handleMergeButtonClick(false, true)"
class="accept-merge-request"
href="#">
<span class="media">
<span
v-html="warningSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="media-body merge-opt-title">Merge immediately</span>
</span>
</a>
</li>
</ul>
</span>
</button>
<ul
v-if="shouldShowMergeOptionsDropdown"
class="dropdown-menu dropdown-menu-right"
role="menu">
<li>
<a
@click.prevent="handleMergeButtonClick(true)"
class="merge_when_pipeline_succeeds"
href="#">
<span
v-html="successSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="merge-opt-title">Merge when pipeline succeeds</span>
</a>
</li>
<li>
<a
@click.prevent="handleMergeButtonClick(false, true)"
class="accept-merge-request"
href="#">
<span
v-html="warningSvg"
class="merge-opt-icon"
aria-hidden="true"></span>
<span class="merge-opt-title">Merge immediately</span>
</a>
</li>
</ul>
</span>
<template v-if="isMergeAllowed()">
<label class="spacing">
<input
id="remove-source-branch-input"
v-model="removeSourceBranch"
:disabled="isRemoveSourceBranchButtonDisabled"
type="checkbox"/> Remove source branch
</label>
<div class="media-body space-children">
<template v-if="isMergeAllowed()">
<label>
<input
id="remove-source-branch-input"
v-model="removeSourceBranch"
:disabled="isRemoveSourceBranchButtonDisabled"
type="checkbox"/> Remove source branch
</label>
<!-- Placeholder for EE extension of this component -->
<squash-before-merge
v-if="shouldShowSquashBeforeMerge"
:mr="mr"
:is-merge-button-disabled="isMergeButtonDisabled" />
<!-- Placeholder for EE extension of this component -->
<squash-before-merge
v-if="shouldShowSquashBeforeMerge"
:mr="mr"
:is-merge-button-disabled="isMergeButtonDisabled" />
<button
@click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled"
class="btn btn-default btn-xs"
type="button">
Modify commit message
</button>
<button
@click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled"
class="btn btn-default btn-xs"
type="button">
Modify commit message
</button>
</template>
<template v-else>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
</span>
</template>
</div>
</div>
<div
v-if="showCommitMessageEditor"
class="prepend-top-default commit-message-editor">
@ -293,7 +310,7 @@ export default {
rows="14"
name="Commit message"></textarea>
</div>
<p class="hint">Try to keep the first line under 52 characters and the others under 72.</p>
<p class="hint">Try to keep the first line under 52 characters and the others under 72</p>
<div class="hint">
<a
@click.prevent="updateCommitMessage"
@ -302,12 +319,7 @@ export default {
</div>
</div>
</div>
</template>
<template v-else>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure.
</span>
</template>
</div>
</div>
`,
};

View File

@ -1,16 +1,18 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetSHAMismatch',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold">
The source branch HEAD has recently changed. Please reload the page and review the changes before merging.
</span>
<div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
The source branch HEAD has recently changed. Please reload the page and review the changes before merging
</span>
</div>
</div>
`,
};

View File

@ -1,27 +1,27 @@
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetUnresolvedDiscussions',
props: {
mr: { type: Object, required: true },
},
components: {
statusIcon,
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold">
There are unresolved discussions. Please resolve these discussions
<span v-if="mr.canCreateIssue">or</span>
<span v-else>.</span>
</span>
<a
v-if="mr.createIssueToResolveDiscussionsPath"
:href="mr.createIssueToResolveDiscussionsPath"
class="btn btn-default btn-xs js-create-issue">
Create an issue to resolve them later
</a>
<div class="mr-widget-body media">
<status-icon status="failed" showDisabledButton />
<div class="media-body space-children">
<span class="bold">
There are unresolved discussions. Please resolve these discussions
</span>
<a
v-if="mr.createIssueToResolveDiscussionsPath"
:href="mr.createIssueToResolveDiscussionsPath"
class="btn btn-default btn-xs js-create-issue">
Create an issue to resolve them later
</a>
</div>
</div>
`,
};

View File

@ -1,4 +1,6 @@
/* global Flash */
import statusIcon from '../mr_widget_status_icon';
import tooltip from '../../../vue_shared/directives/tooltip';
import eventHub from '../../event_hub';
export default {
@ -7,11 +9,17 @@ export default {
mr: { type: Object, required: true },
service: { type: Object, required: true },
},
directives: {
tooltip,
},
data() {
return {
isMakingRequest: false,
};
},
components: {
statusIcon,
},
methods: {
removeWIP() {
this.isMakingRequest = true;
@ -29,20 +37,20 @@ export default {
},
},
template: `
<div class="mr-widget-body">
<button
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge</button>
<span class="bold">
This merge request is currently Work In Progress and therefore unable to merge
</span>
<template v-if="mr.removeWIPPath">
<i
class="fa fa-question-circle has-tooltip"
title="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged." />
<div class="mr-widget-body media">
<status-icon status="failed" :showDisabledButton="Boolean(mr.removeWIPPath)" />
<div class="media-body space-children">
<span class="bold">
This is a Work in Progress
<i
v-tooltip
class="fa fa-question-circle"
title="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"
aria-label="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged">
</i>
</span>
<button
v-if="mr.removeWIPPath"
@click="removeWIP"
:disabled="isMakingRequest"
type="button"
@ -53,7 +61,7 @@ export default {
aria-hidden="true" />
Resolve WIP status
</button>
</template>
</div>
</div>
`,
};

View File

@ -1,7 +1,7 @@
/**
* This file is the centerpiece of an attempt to reduce potential conflicts
* between the CE and EE versions of the MR widget. EE additions to the MR widget should
* be contained in the ./vue_merge_request_widget/ee directory, and should **extend**
* be contained in the ee/vue_merge_request_widget directory, and should **extend**
* rather than mutate CE MR Widget code.
*
* This file should be the only source of conflicts between EE and CE. EE-only components should

View File

@ -35,8 +35,14 @@ import {
export default {
el: '#js-vue-mr-widget',
name: 'MRWidget',
props: {
mrData: {
type: Object,
required: false,
},
},
data() {
const store = new MRWidgetStore(gl.mrWidgetData);
const store = new MRWidgetStore(this.mrData || window.gl.mrWidgetData);
const service = this.createService(store);
return {
mr: store,
@ -234,14 +240,21 @@ export default {
v-if="shouldRenderDeployments"
:mr="mr"
:service="service" />
<component
:is="componentName"
:mr="mr"
:service="service" />
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:related-links="mr.relatedLinks" />
<mr-widget-merge-help v-if="shouldRenderMergeHelp" />
<div class="mr-widget-section">
<component
:is="componentName"
:mr="mr"
:service="service" />
<mr-widget-related-links
v-if="shouldRenderRelatedLinks"
:state="mr.state"
:related-links="mr.relatedLinks" />
</div>
<div
class="mr-widget-footer"
v-if="shouldRenderMergeHelp">
<mr-widget-merge-help />
</div>
</div>
`,
};

View File

@ -26,6 +26,7 @@
@import "framework/lists";
@import "framework/logo";
@import "framework/markdown_area";
@import "framework/media_object";
@import "framework/mobile";
@import "framework/modal";
@import "framework/nav";

View File

@ -45,6 +45,7 @@
margin-top: -23px;
float: right;
font-size: 12px;
direction: ltr;
}
.pika-single.gitlab-theme {

View File

@ -574,6 +574,7 @@
.dropdown-input-field,
.default-dropdown-input {
display: block;
width: 100%;
min-height: 30px;
padding: 0 7px;
@ -722,3 +723,57 @@
@include set-invisible;
overflow: hidden;
}
// TODO: change global style and remove mixin
@mixin new-style-dropdown {
.dropdown-menu,
.dropdown-menu-nav {
.divider {
margin: 6px 0;
}
li {
padding: 0 1px;
&.dropdown-header {
padding: 8px 16px;
}
a {
border-radius: 0;
padding: 8px 16px;
&.is-focused,
&:hover,
&:active,
&:focus {
background-color: $gray-darker;
}
&.is-active {
font-weight: inherit;
&::before {
top: 16px;
}
}
}
}
&.dropdown-menu-selectable {
li {
a {
padding: 8px 40px;
&.is-active::before {
left: 16px;
}
}
}
}
}
.dropdown-menu-align-right {
margin-top: 2px;
}
}

View File

@ -4,6 +4,8 @@
*/
header {
@include new-style-dropdown;
transition: padding $sidebar-transition-duration;
&.navbar-empty {
@ -24,7 +26,7 @@ header {
&.navbar-gitlab {
padding: 0 16px;
z-index: 400;
z-index: 2000;
margin-bottom: 0;
min-height: $header-height;
background-color: $gray-light;
@ -313,25 +315,6 @@ header {
.impersonation i {
color: $red-500;
}
// TODO: fallback to global style
.dropdown-menu,
.dropdown-menu-nav {
li {
padding: 0 1px;
a {
border-radius: 0;
padding: 8px 16px;
&:hover,
&:active,
&:focus {
background-color: $gray-darker;
}
}
}
}
}
.with-performance-bar header.navbar-gitlab {
@ -342,9 +325,9 @@ header {
li {
.badge {
position: inherit;
top: -3px;
top: -8px;
font-weight: normal;
margin-left: -12px;
margin-left: -11px;
font-size: 11px;
color: $white-light;
padding: 1px 5px 2px;

View File

@ -0,0 +1,8 @@
.media {
display: flex;
align-items: flex-start;
}
.media-body {
flex: 1;
}

View File

@ -207,7 +207,6 @@ $general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
$performance-bar-height: 35px;
/*
* Common component specific colors
*/
@ -316,6 +315,12 @@ $btn-white-active: #848484;
$badge-bg: rgba(0, 0, 0, 0.07);
$badge-color: $gl-text-color-secondary;
/*
* Status icons
*/
$status-icon-size: 22px;
$status-icon-margin: $gl-btn-padding;
/*
* Award emoji
*/

View File

@ -312,6 +312,10 @@ header.navbar-gitlab-new {
// TODO: fallback to global style
.dropdown-menu {
.divider {
margin: 6px 0;
}
li {
padding: 0 1px;

Some files were not shown because too many files have changed in this diff Show More