Commit 03495dd0 authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master' into build-chunks-on-object-storage

parents d7a3180d fb08183e
......@@ -14,3 +14,12 @@ lib/gitlab/gitaly_client/ref_service.rb
lib/gitlab/gitaly_client/commit_service.rb
lib/gitlab/git/commit.rb
lib/gitlab/git/tag.rb
ee/db/**/*
ee/app/serializers/ee/merge_request_widget_entity.rb
ee/lib/api/epics.rb
ee/lib/api/geo_nodes.rb
ee/lib/ee/gitlab/ldap/sync/admin_users.rb
ee/app/workers/geo/file_download_dispatch_worker/job_artifact_job_finder.rb
ee/app/workers/geo/file_download_dispatch_worker/lfs_object_job_finder.rb
ee/spec/**/*
......@@ -264,10 +264,10 @@ package-and-qa:
<<: *single-script-job
variables:
<<: *single-script-job-variables
SCRIPT_NAME: trigger-build-omnibus
SCRIPT_NAME: trigger-build
retry: 0
script:
- ./$SCRIPT_NAME
- ./$SCRIPT_NAME omnibus
when: manual
only:
- //@gitlab-org/gitlab-ce
......@@ -415,6 +415,7 @@ setup-test-env:
script:
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
- scripts/gitaly-test-build # Do not use 'bundle exec' here
- BUNDLE_GEMFILE=Gemfile.rails5 bundle install $BUNDLE_INSTALL_FLAGS
artifacts:
expire_in: 7d
paths:
......@@ -590,6 +591,12 @@ downtime_check:
- /(^docs[\/-].*|.*-docs$)/
- /(^qa[\/-].*|.*-qa$)/
rails5_gemfile_lock_check:
<<: *dedicated-no-docs-no-db-pull-cache-job
<<: *except-docs-and-qa
script:
- scripts/rails5-gemfile-lock-check
ee_compat_check:
<<: *rake-exec
except:
......
......@@ -487,7 +487,7 @@ Style/EmptyLiteral:
- 'lib/gitlab/fogbugz_import/importer.rb'
- 'lib/gitlab/git/diff_collection.rb'
- 'lib/gitlab/gitaly_client.rb'
- 'scripts/trigger-build-omnibus'
- 'scripts/trigger-build'
- 'spec/features/merge_requests/versions_spec.rb'
- 'spec/helpers/merge_requests_helper_spec.rb'
- 'spec/lib/gitlab/request_context_spec.rb'
......
......@@ -301,9 +301,9 @@ For guidance on UX implementation at GitLab, please refer to our [Design System]
The UX team uses labels to manage their workflow.
The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/ux/) of the handbook.
To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
Once an issue has been worked on and is ready for development, a UXer applies the ~"UX ready" label to that issue.
Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
The UX team has a special type label called ~"design artifact". This label indicates that the final output
for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
......
......@@ -296,7 +296,7 @@ GEM
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
multi_json
gitlab-gollum-lib (4.2.7.2)
gitlab-gollum-lib (4.2.7.4)
gemojione (~> 3.2)
github-markup (~> 1.6)
gollum-grit_adapter (~> 1.0)
......@@ -304,7 +304,7 @@ GEM
rouge (~> 3.1)
sanitize (~> 2.1)
stringex (~> 2.6)
gitlab-gollum-rugged_adapter (0.4.4)
gitlab-gollum-rugged_adapter (0.4.4.1)
mime-types (>= 1.15)
rugged (~> 0.25)
gitlab-grit (2.8.2)
......
......@@ -315,7 +315,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.3)
gitlab-markup (1.6.4)
gitlab-styles (2.3.2)
rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0)
......@@ -751,36 +751,36 @@ GEM
chunky_png
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
rspec (3.6.0)
rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-core (3.6.0)
rspec-support (~> 3.6.0)
rspec-expectations (3.6.0)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0)
rspec-mocks (3.6.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.6.0)
rspec-support (~> 3.7.0)
rspec-parameterized (0.4.0)
binding_of_caller
parser
proc_to_ast
rspec (>= 2.13, < 4)
unparser
rspec-rails (3.6.0)
rspec-rails (3.7.2)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-support (~> 3.6.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-support (~> 3.7.0)
rspec-retry (0.4.5)
rspec-core
rspec-set (0.1.3)
rspec-support (3.6.0)
rspec-support (3.7.1)
rspec_profiling (0.0.5)
activerecord
pg
......@@ -1054,7 +1054,7 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-markup (~> 1.6.2)
gitlab-markup (~> 1.6.4)
gitlab-styles (~> 2.3)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
......@@ -1152,7 +1152,7 @@ DEPENDENCIES
rouge (~> 3.1)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 3.6.0)
rspec-rails (~> 3.7.0)
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
......
......@@ -169,6 +169,7 @@ the stable branch are:
* Fixes for [regressions](#regressions)
* Fixes for security issues
* Fixes or improvements to automated QA scenarios
* Documentation updates for changes in the same release
* New or updated translations (as long as they do not touch application code)
During the feature freeze all merge requests that are meant to go into the
......@@ -185,11 +186,7 @@ next patch release.
If a merge request is to be picked into more than one release it will need one
`Pick into X.Y` label per release where the merge request should be back-ported
to.
For example, if the current patch release is `10.1.1` and a regression fix needs
to be backported down to the `9.5` release, you will need to assign it the
`10.1` milestone and the following labels:
to. For example:
- `Pick into 10.1`
- `Pick into 10.0`
......
......@@ -72,11 +72,11 @@ export default {
rel="noopener noreferrer"
>
<img
class="project-badge"
:src="imageUrlWithRetries"
class="project-badge"
aria-hidden="true"
@load="onLoad"
@error="onError"
aria-hidden="true"
/>
</a>
......@@ -91,9 +91,9 @@ export default {
>
<div class="btn btn-default btn-sm disabled">
<icon
:size="16"
class="prepend-left-8 append-right-8"
name="doc_image"
:size="16"
aria-hidden="true"
/>
</div>
......@@ -105,16 +105,16 @@ export default {
</div>
<button
v-tooltip
v-show="hasError"
:title="s__('Badges|Reload badge image')"
class="btn btn-transparent btn-sm text-primary"
type="button"
v-tooltip
:title="s__('Badges|Reload badge image')"
@click="reloadImage"
>
<icon
name="retry"
:size="16"
name="retry"
/>
</button>
</div>
......
......@@ -153,10 +153,10 @@ export default {
<label for="badge-link-url">{{ s__('Badges|Link') }}</label>
<input
id="badge-link-url"
type="text"
class="form-control"
v-model="linkUrl"
:placeholder="$options.badgeLinkUrlPlaceholder"
type="text"
class="form-control"
@input="debouncedPreview"
/>
<span
......@@ -169,10 +169,10 @@ export default {
<label for="badge-image-url">{{ s__('Badges|Badge image URL') }}</label>
<input
id="badge-image-url"
type="text"
class="form-control"
v-model="imageUrl"
:placeholder="$options.badgeImageUrlPlaceholder"
type="text"
class="form-control"
@input="debouncedPreview"
/>
<span
......@@ -184,8 +184,8 @@ export default {
<div class="form-group">
<label for="badge-preview">{{ s__('Badges|Badge image preview') }}</label>
<badge
id="badge-preview"
v-show="renderedBadge && !isRendering"
id="badge-preview"
:image-url="renderedImageUrl"
:link-url="renderedLinkUrl"
/>
......@@ -202,16 +202,16 @@ export default {
<div class="row-content-block">
<loading-button
type="submit"
container-class="btn btn-success"
:disabled="!canSubmit"
:loading="isSaving"
:label="submitButtonLabel"
type="submit"
container-class="btn btn-success"
/>
<button
v-if="isEditing"
class="btn btn-cancel"
type="button"
v-if="isEditing"
@click="onCancel"
>{{ __('Cancel') }}</button>
</div>
......
......@@ -41,9 +41,9 @@ export default {
<template>
<div class="gl-responsive-table-row-layout gl-responsive-table-row">
<badge
class="table-section section-30"
:image-url="badge.renderedImageUrl"
:link-url="badge.renderedLinkUrl"
class="table-section section-30"
/>
<span class="table-section section-50 str-truncated">{{ badge.linkUrl }}</span>
<div class="table-section section-10">
......@@ -54,29 +54,29 @@ export default {
v-if="canEditBadge"
class="table-action-buttons">
<button
:disabled="badge.isDeleting"
class="btn btn-default append-right-8"
type="button"
:disabled="badge.isDeleting"
@click="editBadge(badge)"
>
<icon
name="pencil"
:size="16"
:aria-label="__('Edit')"
name="pencil"
/>
</button>
<button
:disabled="badge.isDeleting"
class="btn btn-danger"
type="button"
data-toggle="modal"
data-target="#delete-badge-modal"
:disabled="badge.isDeleting"
@click="updateBadgeInModal(badge)"
>
<icon
name="remove"
:size="16"
:aria-label="__('Delete')"
name="remove"
/>
</button>
<loading-icon
......
......@@ -44,8 +44,8 @@ export default {
<gl-modal
id="delete-badge-modal"
:header-title-text="s__('Badges|Delete badge?')"
footer-primary-button-variant="danger"
:footer-primary-button-text="s__('Badges|Delete badge')"
footer-primary-button-variant="danger"
@submit="onSubmitModal">
<div class="well">
<badge
......
......@@ -119,7 +119,7 @@ const gfmRules = {
return el.outerHTML;
},
'dl'(el, text) {
let lines = text.trim().split('\n');
let lines = text.replace(/\n\n/g, '\n').trim().split('\n');
// Add two spaces to the front of subsequent list items lines,
// or leave the line entirely blank.
lines = lines.map((l) => {
......@@ -129,9 +129,13 @@ const gfmRules = {
return ` ${line}`;
});
return `<dl>\n${lines.join('\n')}\n</dl>`;
return `<dl>\n${lines.join('\n')}\n</dl>\n`;
},
'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) {
'dt, dd, summary, details'(el, text) {
const tag = el.nodeName.toLowerCase();
return `<${tag}>${text}</${tag}>\n`;
},
'sup, sub, kbd, q, samp, var, ruby, rt, rp, abbr'(el, text) {
const tag = el.nodeName.toLowerCase();
return `<${tag}>${text}</${tag}>`;
},
......@@ -215,22 +219,22 @@ const gfmRules = {
return text.replace(/^- /mg, '1. ');
},
'h1'(el, text) {
return `# ${text.trim()}`;
return `# ${text.trim()}\n`;
},
'h2'(el, text) {
return `## ${text.trim()}`;
return `## ${text.trim()}\n`;
},
'h3'(el, text) {
return `### ${text.trim()}`;
return `### ${text.trim()}\n`;
},
'h4'(el, text) {
return `#### ${text.trim()}`;
return `#### ${text.trim()}\n`;
},
'h5'(el, text) {
return `##### ${text.trim()}`;
return `##### ${text.trim()}\n`;
},
'h6'(el, text) {
return `###### ${text.trim()}`;
return `###### ${text.trim()}\n`;
},
'strong'(el, text) {
return `**${text}**`;
......@@ -241,11 +245,13 @@ const gfmRules = {
'del'(el, text) {
return `~~${text}~~`;
},
'sup'(el, text) {
return `^${text}`;
},
'hr'(el) {
return '-----';
// extra leading \n is to ensure that there is a blank line between
// a list followed by an hr, otherwise this breaks old redcarpet rendering
return '\n-----\n';
},
'p'(el, text) {
return `${text.trim()}\n`;
},
'table'(el) {
const theadEl = el.querySelector('thead');
......@@ -263,7 +269,9 @@ const gfmRules = {
let before = '';
let after = '';
switch (cell.style.textAlign) {
const alignment = cell.align || cell.style.textAlign;
switch (alignment) {
case 'center':
before = ':';
after = ':';
......
......@@ -14,17 +14,28 @@ window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.Board = Vue.extend({
template: '#js-board-template',
components: {
boardList,
'board-delete': gl.issueBoards.BoardDelete,
BoardBlankState,
},
props: {
list: Object,
disabled: Boolean,
issueLinkBase: String,
rootPath: String,
list: {
type: Object,
default: () => ({}),
},
disabled: {
type: Boolean,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
boardId: {
type: String,
required: true,
......@@ -82,20 +93,6 @@ gl.issueBoards.Board = Vue.extend({
deep: true
}
},
methods: {
showNewIssueForm() {
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
},
toggleExpanded(e) {
if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
this.list.isExpanded = !this.list.isExpanded;
if (AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
}
}
},
},
mounted () {
this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
disabled: this.disabled,
......@@ -125,4 +122,19 @@ gl.issueBoards.Board = Vue.extend({
this.list.isExpanded = !isCollapsed;
}
},
methods: {
showNewIssueForm() {
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
},
toggleExpanded(e) {
if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
this.list.isExpanded = !this.list.isExpanded;
if (AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
}
}
},
},
template: '#js-board-template',
});
......@@ -72,8 +72,8 @@ export default {
:key="index"
>
<span
class="label-color"
:style="{ backgroundColor: label.color }">
:style="{ backgroundColor: label.color }"
class="label-color">
</span>
{{ label.title }}
</li>
......
......@@ -77,7 +77,6 @@ export default {
<template>
<li
class="board-card"
:class="{
'user-can-drag': !disabled && issue.id,
'is-disabled': disabled || !issue.id,
......@@ -85,6 +84,7 @@ export default {
}"
:index="index"
:data-issue-id="issue.id"
class="board-card"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)">
......
......@@ -8,7 +8,10 @@ window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardDelete = Vue.extend({
props: {
list: Object
list: {
type: Object,
default: () => ({}),
},
},
methods: {
deleteBoard () {
......
......@@ -205,22 +205,22 @@ export default {
<template>
<div class="board-list-component">
<div
v-if="loading"
class="board-list-loading text-center"
aria-label="Loading issues"
v-if="loading">
aria-label="Loading issues">
<loading-icon />
</div>
<board-new-issue
v-if="list.type !== 'closed' && showIssueForm"
:group-id="groupId"
:list="list"
v-if="list.type !== 'closed' && showIssueForm"/>
:list="list"/>
<ul
class="board-list js-board-list"
v-show="!loading"
ref="list"
:data-board="list.id"
:data-board-type="list.type"
:class="{ 'is-smaller': showIssueForm }">
:class="{ 'is-smaller': showIssueForm }"
class="board-list js-board-list">
<board-card
v-for="(issue, index) in issues"
ref="issue"
......@@ -233,8 +233,8 @@ export default {
:disabled="disabled"
:key="issue.id" />
<li
class="board-list-count text-center"
v-if="showCount"
class="board-list-count text-center"
data-issue-id="-1">
<loading-icon
v-show="list.loadingMore"
......
......@@ -96,26 +96,26 @@ export default {
<div class="board-card">
<form @submit="submit($event)">
<div
class="flash-container"
v-if="error"
class="flash-container"
>
<div class="flash-alert">
An error occurred. Please try again.
</div>
</div>
<label
class="label-light"
:for="list.id + '-title'"
class="label-light"
>
Title
</label>
<input
ref="input"
v-model="title"
:id="list.id + '-title'"
class="form-control"
type="text"
v-model="title"
ref="input"
autocomplete="off"
:id="list.id + '-title'"
/>
<project-select
v-if="groupId"
......@@ -123,10 +123,10 @@ export default {
/>
<div class="clearfix prepend-top-10">
<button
ref="submit-button"
:disabled="disabled"
class="btn btn-success float-left"
type="submit"
:disabled="disabled"
ref="submit-button"
>
Submit issue
</button>
......
......@@ -21,8 +21,17 @@ window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardSidebar = Vue.extend({
components: {
assigneeTitle,
assignees,
removeBtn: gl.issueBoards.RemoveIssueBtn,
subscriptions,
},
props: {
currentUser: Object
currentUser: {
type: Object,
default: () => ({}),
},
},
data() {
return {
......@@ -64,6 +73,26 @@ gl.issueBoards.BoardSidebar = Vue.extend({
deep: true
},
},
created () {
// Get events from glDropdown
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
eventHub.$on('sidebar.addAssignee', this.addAssignee);
eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$on('sidebar.saveAssignees', this.saveAssignees);
},
beforeDestroy() {
eventHub.$off('sidebar.removeAssignee', this.removeAssignee);
eventHub.$off('sidebar.addAssignee', this.addAssignee);
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
},
mounted () {
new IssuableContext(this.currentUser);
new MilestoneSelect();
new DueDateSelectors();
new LabelsSelect();
new Sidebar();
},
methods: {
closeSidebar () {
this.detail.issue = {};
......@@ -97,30 +126,4 @@ gl.issueBoards.BoardSidebar = Vue.extend({
});
},
},
created () {
// Get events from glDropdown
eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
eventHub.$on('sidebar.addAssignee', this.addAssignee);
eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$on('sidebar.saveAssignees', this.saveAssignees);
},
beforeDestroy() {
eventHub.$off('sidebar.removeAssignee', this.removeAssignee);
eventHub.$off('sidebar.addAssignee', this.addAssignee);
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
},
mounted () {
new IssuableContext(this.currentUser);
new MilestoneSelect();
new DueDateSelectors();
new LabelsSelect();
new Sidebar();
},
components: {
assigneeTitle,
assignees,
removeBtn: gl.issueBoards.RemoveIssueBtn,
subscriptions,
},
});
......@@ -9,6 +9,9 @@ window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.IssueCardInner = Vue.extend({
components: {
UserAvatarLink,
},
props: {
issue: {
type: Object,
......@@ -35,6 +38,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
groupId: {
type: Number,
required: false,
default: null,
},
},
data() {
......@@ -44,9 +48,6 @@ gl.issueBoards.IssueCardInner = Vue.extend({
maxCounter: 99,
};
},
components: {
UserAvatarLink,
},
computed: {
numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter;
......
......@@ -4,9 +4,6 @@ import modalMixin from '../../mixins/modal_mixins';
gl.issueBoards.ModalEmptyState = Vue.extend({
mixins: [modalMixin],
data() {
return ModalStore.store;
},
props: {
newIssuePath: {
type: String,
......@@ -17,6 +14,9 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
required: true,
},
},
data() {
return ModalStore.store;
},
computed: {
contents() {
const obj = {
......
......@@ -7,6 +7,9 @@ import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
gl.issueBoards.ModalFooter = Vue.extend({
components: {
'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
},
mixins: [modalMixin],
data() {
return {
......@@ -52,9 +55,6 @@ gl.issueBoards.ModalFooter = Vue.extend({
this.toggleModal(false);
},
},
components: {
'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
},
template: `
<footer
class="form-actions add-issues-footer">
......
......@@ -5,6 +5,10 @@ import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
gl.issueBoards.ModalHeader = Vue.extend({
components: {
'modal-tabs': gl.issueBoards.ModalTabs,
modalFilters,
},
mixins: [modalMixin],
props: {
projectId: {
......@@ -42,10 +46,6 @@ gl.issueBoards.ModalHeader = Vue.extend({
ModalStore.toggleAll();
},
},
components: {
'modal-tabs': gl.issueBoards.ModalTabs,
modalFilters,
},
template: `
<div>
<header class="add-issues-header form-actions">
......
......@@ -10,6 +10,13 @@ import './empty_state';
import ModalStore from '../../stores/modal_store';
gl.issueBoards.IssuesModal = Vue.extend({
components: {
'modal-header': gl.issueBoards.ModalHeader,
'modal-list': gl.issueBoards.ModalList,
'modal-footer': gl.issueBoards.ModalFooter,
'empty-state': gl.issueBoards.ModalEmptyState,
loadingIcon,
},
props: {
newIssuePath: {
type: String,
......@@ -43,6 +50,22 @@ gl.issueBoards.IssuesModal = Vue.extend({
data() {
return ModalStore.store;
},
computed: {
showList() {
if (this.activeTab === 'selected') {
return this.selectedIssues.length > 0;
}
return this.issuesCount > 0;
},
showEmptyState() {
if (!this.loading && this.issuesCount === 0) {
return true;
}
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
},
watch: {
page() {
this.loadIssues();
......@@ -80,6 +103,9 @@ gl.issueBoards.IssuesModal = Vue.extend({
deep: true,
},
},
created() {
this.page = 1;
},
methods: {
loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false;
......@@ -112,32 +138,6 @@ gl.issueBoards.IssuesModal = Vue.extend({
});
},
},
computed: {
showList() {
if (this.activeTab === 'selected') {
return this.selectedIssues.length > 0;
}
return this.issuesCount > 0;
},
showEmptyState() {
if (!this.loading && this.issuesCount === 0) {
return true;
}
return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
},
created() {
this.page = 1;
},
components: {
'modal-header': gl.issueBoards.ModalHeader,
'modal-list': gl.issueBoards.ModalList,
'modal-footer': gl.issueBoards.ModalFooter,
'empty-state': gl.issueBoards.ModalEmptyState,
loadingIcon,
},
template: `
<div
class="add-issues-modal"
......
......@@ -3,6 +3,9 @@ import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store';
gl.issueBoards.ModalList = Vue.extend({
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
props: {
issueLinkBase: {
type: String,
......@@ -20,13 +23,6 @@ gl.issueBoards.ModalList = Vue.extend({
data() {
return ModalStore.store;
},
watch: {
activeTab() {
if (this.activeTab === 'all') {
ModalStore.purgeUnselectedIssues();
}
},
},
computed: {
loopIssues() {
if (this.activeTab === 'all') {
......@@ -50,6 +46,25 @@ gl.issueBoards.ModalList = Vue.extend({
return groups;
},
},
watch: {
activeTab() {
if (this.activeTab === 'all') {
ModalStore.purgeUnselectedIssues();
}
},
},
mounted() {
this.scrollHandlerWrapper = this.scrollHandler.bind(this);
this.setColumnCountWrapper = this.setColumnCount.bind(this);
this.setColumnCount();
this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
window.addEventListener('resize', this.setColumnCountWrapper);
},
beforeDestroy() {
this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
window.removeEventListener('resize', this.setColumnCountWrapper);
},
methods: {
scrollHandler() {
const currentPage = Math.floor(this.issues.length / this.perPage);
......@@ -96,21 +111,6 @@ gl.issueBoards.ModalList = Vue.extend({
}
},
},
mounted() {
this.scrollHandlerWrapper = this.scrollHandler.bind(this);
this.setColumnCountWrapper = this.setColumnCount.bind(this);
this.setColumnCount();
this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
window.addEventListener('resize', this.setColumnCountWrapper);
},
beforeDestroy() {
this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
window.removeEventListener('resize', this.setColumnCountWrapper);
},
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
template: `
<section
class="add-issues-list add-issues-list-columns"
......
......@@ -55,7 +55,8 @@ class List {
entityType = 'assignee_id';
}
return gl.boardService.createList(this.label.id)
return gl.boardService
.createList(entity.id, entityType)
.then(res => res.data)
.then(data => {
this.id = data.id;
......
......@@ -125,8 +125,8 @@
<template>
<div
class="gl-responsive-table-row gl-responsive-table-row-col-span"
:class="rowJsClass"
class="gl-responsive-table-row gl-responsive-table-row-col-span"
>
<div
class="gl-responsive-table-row-layout"
......@@ -155,8 +155,8 @@
<slot name="description"></slot>
</div>
<div
class="table-section table-button-footer section-align-top"
:class="{ 'section-20': showManageButton, 'section-15': !showManageButton }"
class="table-section table-button-footer section-align-top"
role="gridcell"
>
<div
......@@ -164,18 +164,18 @@
class="btn-group table-action-buttons"
>
<a
class="btn"
:href="manageLink"
class="btn"
>
{{ manageButtonLabel }}
</a>
</div>
<div class="btn-group table-action-buttons">
<loading-button
class="js-cluster-application-install-button"
:loading="installButtonLoading"
:disabled="installButtonDisabled"
:label="installButtonLabel"
class="js-cluster-application-install-button"
@click="installClicked"
/>
</div>
......
......@@ -152,11 +152,11 @@ export default {
<application-row
id="helm"
:title="applications.helm.title"
title-link="https://docs.helm.sh/"
:status="applications.helm.status"
:status-reason="applications.helm.statusReason"
:request-status="applications.helm.requestStatus"
:request-reason="applications.helm.requestReason"
title-link="https://docs.helm.sh/"
>
<div slot="description">
{{ s__(`ClusterIntegration|Helm streamlines installing
......@@ -168,11 +168,11 @@ export default {
<application-row
:id="ingressId"
:title="applications.ingress.title"
title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
:status="applications.ingress.status"
:status-reason="applications.ingress.statusReason"
:request-status="applications.ingress.requestStatus"
:request-reason="applications.ingress.requestReason"
title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
>
<div slot="description">
<p>
......@@ -191,10 +191,10 @@ export default {
class="input-group"
>
<input
type="text"
id="ingress-ip-address"
class="form-control js-ip-address"
:value="ingressExternalIp"
type="text"
class="form-control js-ip-address"
readonly
/>
<span class="input-group-append">
......@@ -255,12 +255,12 @@ export default {
<application-row
id="prometheus"
:title="applications.prometheus.title"
title-link="https://prometheus.io/docs/introduction/overview/"
:manage-link="managePrometheusPath"
:status="applications.prometheus.status"
:status-reason="applications.prometheus.statusReason"
:request-status="applications.prometheus.requestStatus"
:request-reason="applications.prometheus.requestReason"
title-link="https://prometheus.io/docs/introduction/overview/"
>
<div
slot="description"
......@@ -271,11 +271,11 @@ export default {
<application-row
id="runner"
:title="applications.runner.title"
title-link="https://docs.gitlab.com/runner/"
:status="applications.runner.status"
:status-reason="applications.runner.statusReason"
:request-status="applications.runner.requestStatus"
:request-reason="applications.runner.requestReason"
title-link="https://docs.gitlab.com/runner/"
>
<div slot="description">
{{ s__(`ClusterIntegration|GitLab Runner connects to this
......@@ -287,12 +287,12 @@ export default {
<application-row
id="jupyter"
:title="applications.jupyter.title"
title-link="https://jupyterhub.readthedocs.io/en/stable/"
:status="applications.jupyter.status"
:status-reason="applications.jupyter.statusReason"
:request-status="applications.jupyter.requestStatus"
:request-reason="applications.jupyter.requestReason"
:install-application-request-params="{ hostname: applications.jupyter.hostname }"
title-link="https://jupyterhub.readthedocs.io/en/stable/"
>
<div slot="description">
<p>
......@@ -311,10 +311,10 @@ export default {
<div class="input-group">
<input
type="text"
class="form-control js-hostname"
v-model="applications.jupyter.hostname"
:readonly="jupyterInstalled"
type="text"
class="form-control js-hostname"
/>
<span
class="input-group-btn"
......
......@@ -77,9 +77,9 @@
<div class="content-list pipelines">
<loading-icon
v-if="isLoading"
:label="s__('Pipelines|Loading Pipelines')"
size="3"
v-if="isLoading"
class="prepend-top-20"
/>
......@@ -91,8 +91,8 @@
/>
<div
class="table-holder"
v-else-if="shouldRenderTable"
class="table-holder"
>
<pipelines-table-component
:pipelines="state.pipelines"
......
......@@ -66,8 +66,14 @@ export default class CreateMergeRequestDropdown {
}
bindEvents() {
this.createMergeRequestButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
this.createTargetButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
this.createMergeRequestButton.addEventListener(
'click',
this.onClickCreateMergeRequestButton.bind(this),
);
this.createTargetButton.addEventListener(
'click',
this.onClickCreateMergeRequestButton.bind(this),
);
this.branchInput.addEventListener('keyup', this.onChangeInput.bind(this));
this.dropdownToggle.addEventListener('click', this.onClickSetFocusOnBranchNameInput.bind(this));
this.refInput.addEventListener('keyup', this.onChangeInput.bind(this));
......@@ -77,7 +83,8 @@ export default class CreateMergeRequestDropdown {
checkAbilityToCreateBranch() {
this.setUnavailableButtonState();
axios.get(this.canCreatePath)
axios
.get(this.canCreatePath)
.then(({ data }) => {
this.setUnavailableButtonState(false);
......@@ -105,7 +112,8 @@ export default class CreateMergeRequestDropdown {
createBranch() {
this.isCreatingBranch = true;
return axios.post(this.createBranchPath)
return axios
.post(this.createBranchPath)
.then(({ data }) => {
this.branchCreated = true;
window.location.href = data.url;
......@@ -116,7 +124,8 @@ export default class CreateMergeRequestDropdown {
createMergeRequest() {
this.isCreatingMergeRequest = true;
return axios.post(this.createMrPath)
return axios
.post(this.createMrPath)
.then(({ data }) => {
this.mergeRequestCreated = true;
window.location.href = data.url;
......@@ -195,7 +204,8 @@ export default class CreateMergeRequestDropdown {
getRef(ref, target = 'all') {
if (!ref) return false;
return axios.get(this.refsPath + ref)
return axios
.get(`${this.refsPath}${encodeURIComponent(ref)}`)
.then(({ data }) => {
const branches = data[Object.keys(data)[0]];
const tags = data[Object.keys(data)[1]];
......@@ -204,7 +214,8 @@ export default class CreateMergeRequestDropdown {
if (target === 'branch') {
result = CreateMergeRequestDropdown.findByValue(branches, ref);
} else {
result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
result =
CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
CreateMergeRequestDropdown.findByValue(tags, ref, true);
this.suggestedRef = result;
}
......@@ -255,11 +266,13 @@ export default class CreateMergeRequestDropdown {
}
isBusy() {
return this.isCreatingMergeRequest ||
return (
this.isCreatingMergeRequest ||
this.mergeRequestCreated ||
this.isCreatingBranch ||
this.branchCreated ||
this.isGettingRef;
this.isGettingRef
);
}
onChangeInput(event) {
......@@ -271,7 +284,8 @@ export default class CreateMergeRequestDropdown {
value = this.branchInput.value;
} else if (event.target === this.refInput) {
target = 'ref';
value = event.target.value.slice(0, event.target.selectionStart) +
value =
event.target.value.slice(0, event.target.selectionStart) +
event.target.value.slice(event.target.selectionEnd);
} else {
return false;
......@@ -396,7 +410,8 @@ export default class CreateMergeRequestDropdown {
showNotAvailableMessage(target) {
const { input, message } = this.getTargetData(target);
const text = target === 'branch' ? __('Branch is already taken') : __('Source is not available');
const text =
target === 'branch' ? __('Branch is already taken') : __('Source is not available');
this.removeMessage(target);
input.classList.add('gl-field-error-outline');
......@@ -459,11 +474,15 @@ export default class CreateMergeRequestDropdown {
// target - 'branch' or 'ref'
// ref - string - the new value to use as branch or ref
updateCreatePaths(target, ref) {
const pathReplacement = `$1${ref}`;
const pathReplacement = `$1${encodeURIComponent(ref)}`;
this.createBranchPath = this.createBranchPath.replace(this.regexps[target].createBranchPath,
pathReplacement);
this.createMrPath = this.createMrPath.replace(this.regexps[target].createMrPath,
pathReplacement);
this.createBranchPath = this.createBranchPath.replace(
this.regexps[target].createBranchPath,
pathReplacement,
);
this.createMrPath = this.createMrPath.replace(
this.regexps[target].createMrPath,
pathReplacement,
);
}
}
......@@ -23,9 +23,9 @@
<template>
<div class="landing content-block">
<button
:aria-label="__('Dismiss Cycle Analytics introduction box')"
class="js-ca-dismiss-button dismiss-button"
type="button"
:aria-label="__('Dismiss Cycle Analytics introduction box')"
@click="dismissOverviewDialog"
>
<i
......
......@@ -19,14 +19,14 @@
class="events-info float-right"
>
<i
class="fa fa-warning"
v-tooltip
aria-hidden="true"
:title="n__(
'Limited to showing %d event at most',
'Limited to showing %d events at most',
50
)"
class="fa fa-warning"
aria-hidden="true"
data-placement="top"
>
</i>
......
......@@ -38,8 +38,8 @@
<user-avatar-image :img-src="issue.author.avatarUrl"/>
<h5 class="item-title issue-title">
<a
class="issue-title"
:href="issue.url"
class="issue-title"
>
{{ issue.title }}
</a>
......
......@@ -74,12 +74,12 @@
</template>
<template v-else>
<span
class="merge-request-branch"
v-if="mergeRequest.branch"
class="merge-request-branch"
>
<icon
name="fork"
:size="16"
name="fork"
/>
<a :href="mergeRequest.branch.url">
{{ mergeRequest.branch.name }}
......
......@@ -38,8 +38,8 @@
<ul class="stage-event-list">
<li
v-for="(build, i) in items"
class="stage-event-item item-build-component"
:key="i"
class="stage-event-item item-build-component"
>
<div class="item-details">
<!-- FIXME: Pass an alt attribute here for accessibility -->
......@@ -52,8 +52,8 @@
#{{ build.id }}
</a>
<icon
name="fork"
:size="16"
name="fork"
/>
<a
:href="build.branch.url"
......
......@@ -64,8 +64,8 @@
#{{ build.id }}
</a>
<icon
name="fork"
:size="16"
name="fork"
/>
<a
:href="build.branch.url"
......
......@@ -40,9 +40,9 @@ export default {
<template>
<button
class="btn"
:class="[{ disabled: isLoading }, btnCssClass]"
:disabled="isLoading"
class="btn"
@click="doAction">
<slot></slot>
<loading-icon
......
......@@ -116,8 +116,8 @@ export default {
<div class="append-bottom-default deploy-keys">
<loading-icon
v-if="isLoading && !hasKeys"
size="2"
:label="s__('DeployKeys|Loading deploy keys')"
size="2"
/>
<template v-else-if="hasKeys">
<div class="top-area scrolling-tabs-container inner-page-scroll-tabs">
......@@ -138,16 +138,16 @@ export default {
<navigation-tabs
:tabs="tabs"
@onChangeTab="onChangeTab"
scope="deployKeys"
@onChangeTab="onChangeTab"
/>
</div>
<keys-panel
class="qa-project-deploy-keys"
:project-id="projectId"
:keys="keys[currentTab]"
:store="store"
:endpoint="endpoint"
class="qa-project-deploy-keys"
/>
</template>
</div>
......
......@@ -135,9 +135,9 @@ export default {
<div class="table-mobile-content deploy-project-list">
<template v-if="projects.length > 0">
<a
class="label deploy-project-label"
:title="projectTooltipTitle(firstProject)"
v-tooltip
:title="projectTooltipTitle(firstProject)"
class="label deploy-project-label"
>
<span>
{{ firstProject.project.full_name }}
......@@ -145,22 +145,22 @@ export default {
<icon :name="firstProject.can_push ? 'lock-open' : 'lock'"/>
</a>
<a
v-tooltip
v-if="isExpandable"
:title="restProjectsTooltip"
class="label deploy-project-label"
@click="toggleExpanded"
:title="restProjectsTooltip"
v-tooltip
>
<span>{{ restProjectsLabel }}</span>
</a>
<a
v-else-if="isExpanded"
v-tooltip
v-for="deployKeysProject in restProjects"
v-else-if="isExpanded"
:key="deployKeysProject.project.full_path"
class="label deploy-project-label"
:href="deployKeysProject.project.full_path"
:title="projectTooltipTitle(deployKeysProject)"
v-tooltip
class="label deploy-project-label"
>
<span>
{{ deployKeysProject.project.full_name }}
......@@ -181,8 +181,8 @@ export default {
</div>
<div class="table-mobile-content text-secondary key-created-at">
<span
:title="tooltipTitle(deployKey.created_at)"
v-tooltip>
v-tooltip
:title="tooltipTitle(deployKey.created_at)">
<icon name="calendar"/>
<span>{{ timeFormated(deployKey.created_at) }}</span>
</span>
......@@ -198,34 +198,34 @@ export default {
{{ __('Enable') }}
</action-btn>
<a
v-tooltip
v-if="deployKey.can_edit"
class="btn btn-default text-secondary"
:href="editDeployKeyPath"
:title="__('Edit')"
class="btn btn-default text-secondary"
data-container="body"
v-tooltip
>
<icon name="pencil"/>
</a>
<action-btn
v-tooltip
v-if="isRemovable"
:deploy-key="deployKey"
:title="__('Remove')"
btn-css-class="btn-danger"
type="remove"
:title="__('Remove')"
data-container="body"
v-tooltip
>
<icon name="remove"/>
</action-btn>
<action-btn
v-tooltip
v-else-if="isEnabled"
:deploy-key="deployKey"
:title="__('Disable')"
btn-css-class="btn-warning"
type="disable"
:title="__('Disable')"
data-container="body"
v-tooltip
>
<icon name="cancel"/>
</action-btn>
......
......@@ -59,8 +59,8 @@ export default {
/>
</template>
<div
class="settings-message text-center"
v-else
class="settings-message text-center"
>
{{ s__('DeployKeys|No deploy keys found. Create one with the form above.') }}
</div>
......
......@@ -6,7 +6,10 @@ import Vue from 'vue';
const CommentAndResolveBtn = Vue.extend({
props: {
discussionId: String,
discussionId: {
type: String,
required: true,
},
},
data() {
return {
......
......@@ -7,7 +7,15 @@ import Notes from '../../notes';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
const DiffNoteAvatars = Vue.extend({
props: ['discussionId'],
components: {
userAvatarImage,
},
props: {
discussionId: {
type: String,
required: true,
},
},
data() {
return {
isVisible: false,
......@@ -17,77 +25,6 @@ const DiffNoteAvatars = Vue.extend({
collapseIcon,
};
},
components: {
userAvatarImage,
},
template: `
<div class="diff-comment-avatar-holders"
:class="discussionClassName"
v-show="notesCount !== 0">
<div v-if="!isVisible">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image
v-for="note in notesSubset"
:key="note.id"
class="diff-comment-avatar js-diff-comment-avatar"
@click.native="clickedAvatar($event)"
:img-src="note.authorAvatar"
:tooltip-text="getTooltipText(note)"
:data-line-type="lineType"
:size="19"
data-html="true"
/>
<span v-if="notesCount > shownAvatars"
class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
data-container="body"
data-placement="top"
ref="extraComments"
role="button"
:data-line-type="lineType"
:title="extraNotesTitle"
@click="clickedAvatar($event)">{{ moreText }}</span>
</div>
<button class="diff-notes-collapse js-diff-comment-avatar"
type="button"
aria-label="Show comments"
:data-line-type="lineType"
@click="clickedAvatar($event)"
v-if="isVisible"
v-html="collapseIcon">
</button>
</div>
`,
mounted() {
this.$nextTick(() => {
this.addNoCommentClass();
this.setDiscussionVisible();
this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
});
$(document).on('toggle.comments', () => {
this.$nextTick(() => {
this.setDiscussionVisible();
});
});
},
beforeDestroy() {
this.addNoCommentClass();
$(document).off('toggle.comments');
},
watch: {
storeState: {
handler() {
this.$nextTick(() => {
$('.has-tooltip', this.$el).tooltip('_fixTitle');
// We need to add/remove a class to an element that is outside the Vue instance
this.addNoCommentClass();
});
},
deep: true,
},
},
computed: {
discussionClassName() {
return `js-diff-avatars-${this.discussionId}`;
......@@ -128,6 +65,37 @@ const DiffNoteAvatars = Vue.extend({
return `${plusSign}${this.notesCount - this.shownAvatars}`;
},
},
watch: {
storeState: {
handler() {
this.$nextTick(() => {
$('.has-tooltip', this.$el).tooltip('_fixTitle');
// We need to add/remove a class to an element that is outside the Vue instance
this.addNoCommentClass();
});
},
deep: true,
},
},
mounted() {
this.$nextTick(() => {
this.addNoCommentClass();
this.setDiscussionVisible();
this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
});
$(document).on('toggle.comments', () => {
this.$nextTick(() => {
this.setDiscussionVisible();
});
});
},
beforeDestroy() {
this.addNoCommentClass();
$(document).off('toggle.comments');
},
methods: {
clickedAvatar(e) {
Notes.instance.onAddDiffNote(e);
......@@ -164,6 +132,43 @@ const DiffNoteAvatars = Vue.extend({
return `${note.authorName}: ${note.noteTruncated}`;
},
},
template: `
<div class="diff-comment-avatar-holders"
:class="discussionClassName"
v-show="notesCount !== 0">
<div v-if="!isVisible">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image
v-for="note in notesSubset"
:key="note.id"
class="diff-comment-avatar js-diff-comment-avatar"
@click.native="clickedAvatar($event)"
:img-src="note.authorAvatar"
:tooltip-text="getTooltipText(note)"
:data-line-type="lineType"
:size="19"
data-html="true"
/>
<span v-if="notesCount > shownAvatars"
class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
data-container="body"
data-placement="top"
ref="extraComments"
role="button"
:data-line-type="lineType"
:title="extraNotesTitle"
@click="clickedAvatar($event)">{{ moreText }}</span>
</div>
<button class="diff-notes-collapse js-diff-comment-avatar"
type="button"
aria-label="Show comments"
:data-line-type="lineType"
@click="clickedAvatar($event)"
v-if="isVisible"
v-html="collapseIcon">
</button>
</div>
`,
});
Vue.component('diff-note-avatars', DiffNoteAvatars);
......@@ -10,7 +10,10 @@ import '../mixins/discussion';
const JumpToDiscussion = Vue.extend({
mixins: [DiscussionMixins],
props: {
discussionId: String
discussionId: {
type: String,
required: true,
},
},
data: function () {
return {
......@@ -52,6 +55,9 @@ const JumpToDiscussion = Vue.extend({
return lastId;
}
},
created() {
this.discussion = this.discussions[this.discussionId];
},
methods: {
jumpToNextUnresolvedDiscussion: function () {
let discussionsSelector;
......@@ -202,9 +208,6 @@ const JumpToDiscussion = Vue.extend({
});
}
},
created() {
this.discussion = this.discussions[this.discussionId];
},
});
Vue.component('jump-to-discussion', JumpToDiscussion);
......@@ -8,14 +8,38 @@ import Flash from '../../flash';
const ResolveBtn = Vue.extend({
props: {
noteId: Number,
discussionId: String,
resolved: Boolean,
canResolve: Boolean,
resolvedBy: String,
authorName: String,
authorAvatar: String,
noteTruncated: String,
noteId: {
type: Number,
required: true,
},
discussionId: {
type: String,
required: true,
},
resolved: {
type: Boolean,
required: true,
},
canResolve: {
type: Boolean,
required: true,
},
resolvedBy: {
type: String,
required: true,
},
authorName: {
type: String,
required: true,
},
authorAvatar: {
type: String,
required: true,
},
noteTruncated: {
type: String,
required: true,
},
},
data: function () {
return {
......@@ -23,12 +47,6 @@ const ResolveBtn = Vue.extend({
loading: false
};
},
watch: {
'discussions': {
handler: 'updateTooltip',
deep: true
}
},
computed: {
discussion: function () {
return this.discussions[this.discussionId];
......@@ -56,6 +74,32 @@ const ResolveBtn = Vue.extend({
return this.note.resolved_by;
},
},
watch: {
'discussions': {
handler: 'updateTooltip',
deep: true
}
},
mounted: function () {
$(this.$refs.button).tooltip({
container: 'body'
});
},
beforeDestroy: function () {
CommentsStore.delete(this.discussionId, this.noteId);
},
created: function () {
CommentsStore.create({
discussionId: this.discussionId,
noteId: this.noteId,
canResolve: this.canResolve,
resolved: this.resolved,
resolvedBy: this.resolvedBy,
authorName: this.authorName,
authorAvatar: this.authorAvatar,
noteTruncated: this.noteTruncated,
});
},
methods: {
updateTooltip: function () {
this.$nextTick(() => {
......@@ -95,26 +139,6 @@ const ResolveBtn = Vue.extend({
.catch(() => new Flash('An error occurred when trying to resolve a comment. Please try again.'));
}
},
mounted: function () {
$(this.$refs.button).tooltip({
container: 'body'
});
},
beforeDestroy: function () {
CommentsStore.delete(this.discussionId, this.noteId);
},
created: function () {
CommentsStore.create({
discussionId: this.discussionId,
noteId: this.noteId,
canResolve: this.canResolve,
resolved: this.resolved,
resolvedBy: this.resolvedBy,
authorName: this.authorName,
authorAvatar: this.authorAvatar,
noteTruncated: this.noteTruncated,
});
}
});
Vue.component('resolve-btn', ResolveBtn);
......@@ -9,7 +9,10 @@ import '../mixins/discussion';
window.ResolveCount = Vue.extend({
mixins: [DiscussionMixins],
props: {
loggedOut: Boolean
loggedOut: {
type: Boolean,
required: true,
},
},
data: function () {
return {
......
......@@ -6,9 +6,18 @@ import Vue from 'vue';
const ResolveDiscussionBtn = Vue.extend({
props: {
discussionId: String,
mergeRequestId: Number,
canResolve: Boolean,
discussionId: {
type: String,
required: true,
},
mergeRequestId: {
type: Number,
required: true,
},
canResolve: {
type: Boolean,
required: true,
},
},
data: function() {
return {
......@@ -45,16 +54,16 @@ const ResolveDiscussionBtn = Vue.extend({
}
}
},
methods: {
resolve: function () {
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
}
},
created: function () {
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
this.discussion = CommentsStore.state[this.discussionId];
},
methods: {
resolve: function () {
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
}
},
});
Vue.component('resolve-discussion-btn', ResolveDiscussionBtn);
......@@ -43,17 +43,17 @@
<div class="environments-container">
<loading-icon
v-if="isLoading"
class="prepend-top-default"
label="Loading environments"
v-if="isLoading"
size="3"
/>
<slot name="emptyState"></slot>
<div
class="table-holder"
v-if="!isLoading && environments.length > 0">
v-if="!isLoading && environments.length > 0"
class="table-holder">
<environment-table
:environments="environments"
......
......@@ -52,18 +52,18 @@
role="group">
<button
v-tooltip
:title="title"
:aria-label="title"
:disabled="isLoading"
type="button"
class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container"
data-container="body"
data-toggle="dropdown"
:title="title"
:aria-label="title"
:disabled="isLoading"
>
<span>
<icon
name="play"
:size="12"
name="play"
/>
<i
class="fa fa-caret-down"
......@@ -79,15 +79,15 @@
v-for="(action, i) in actions"
:key="i">
<button
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
type="button"
class="js-manual-action-link no-btn btn"
@click="onClickAction(action.play_path)"
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
>
<icon
name="play"
:size="12"
name="play"
/>
<span>
{{ action.name }}
......
......@@ -29,17 +29,17 @@
<template>
<a
v-tooltip
:title="title"
:aria-label="title"
:href="externalUrl"
class="btn external-url"
data-container="body"
target="_blank"
rel="noopener noreferrer nofollow"
:title="title"
:aria-label="title"
:href="externalUrl"
>
<icon
name="external-link"
:size="12"
name="external-link"
/>
</a>
</template>
......@@ -427,11 +427,11 @@
</script>
<template>
<div
class="gl-responsive-table-row"
:class="{
'js-child-row environment-child-row': model.isChildren,
'folder-row': model.isFolder,
}"
class="gl-responsive-table-row"
role="row">
<div
class="table-section section-10"
......@@ -446,19 +446,19 @@
</div>
<a
v-if="!model.isFolder"
class="environment-name flex-truncate-parent table-mobile-content"
:href="environmentPath">
:href="environmentPath"
class="environment-name flex-truncate-parent table-mobile-content">
<span
class="flex-truncate-child"
v-tooltip
:title="model.name"
class="flex-truncate-child"
>{{ model.name }}</span>
</a>
<span
v-else
class="folder-name"
@click="onClickFolder"
role="button">
role="button"
@click="onClickFolder">
<span class="folder-icon">
<i
......@@ -503,11 +503,11 @@
<span v-if="!model.isFolder && deploymentHasUser">
by
<user-avatar-link
class="js-deploy-user-container"
:link-href="deploymentUser.web_url"
:img-src="deploymentUser.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="deploymentUser.username"
class="js-deploy-user-container"
/>
</span>
</div>
......@@ -518,8 +518,8 @@
>
<a
v-if="shouldRenderBuildName"
class="build-link flex-truncate-parent"
:href="buildPath"
class="build-link flex-truncate-parent"
>
<span class="flex-truncate-child">{{ buildName }}</span>
</a>
......
......@@ -28,16 +28,16 @@
<template>
<a
v-tooltip
class="btn monitoring-url d-none d-sm-none d-md-block"
data-container="body"
rel="noopener noreferrer nofollow"
:href="monitoringUrl"
:title="title"
:aria-label="title"
class="btn monitoring-url d-none d-sm-none d-md-block"
data-container="body"
rel="noopener noreferrer nofollow"
>
<icon
name="chart"
:size="12"
name="chart"
/>
</a>
</template>
......@@ -39,10 +39,10 @@
</script>
<template>
<button
:disabled="isLoading"
type="button"
class="btn d-none d-sm-none d-md-block"
@click="onClick"
:disabled="isLoading"
>
<span v-if="isLastDeployment">
......
......@@ -54,13 +54,13 @@
<template>
<button
v-tooltip
:disabled="isLoading"
:title="title"
:aria-label="title"
type="button"
class="btn stop-env-link d-none d-sm-none d-md-block"
data-container="body"
@click="onClick"
:disabled="isLoading"
:title="title"
:aria-label="title"
>
<i
class="fa fa-stop stop-env-icon"
......
......@@ -30,15 +30,15 @@
<template>
<a
v-tooltip
class="btn terminal-button d-none d-sm-none d-md-block"
data-container="body"
:title="title"
:aria-label="title"
:href="terminalPath"
class="btn terminal-button d-none d-sm-none d-md-block"
data-container="body"
>
<icon
name="terminal"
:size="12"
name="terminal"
/>
</a>
</template>
......@@ -93,8 +93,8 @@
<div class="top-area">
<tabs
:tabs="tabs"
@onChangeTab="onChangeTab"
scope="environments"
@onChangeTab="onChangeTab"
/>
<div
......@@ -119,8 +119,8 @@
@onChangePage="onChangePage"
>
<empty-state
slot="emptyState"
v-if="!isLoading && state.environments.length === 0"
slot="emptyState"
:new-path="newEnvironmentPath"
:help-path="helpPagePath"
:can-create-environment="canCreateEnvironment"
......
......@@ -39,8 +39,8 @@
<template>
<div :class="cssContainerClass">
<div
class="top-area"
v-if="!isLoading"
class="top-area"
>
<h4 class="js-folder-name environments-folder-name">
......@@ -49,8 +49,8 @@
<tabs
:tabs="tabs"
@onChangeTab="onChangeTab"
scope="environments"
@onChangeTab="onChangeTab"
/>
</div>
......
......@@ -72,9 +72,9 @@ export default {
@click="onItemActivated(item.text)">
<span>
<span
class="filtered-search-history-dropdown-token"
v-for="(token, index) in item.tokens"
:key="`dropdown-token-${index}`"
class="filtered-search-history-dropdown-token"
>
<span class="name">{{ token.prefix }}</span>
<span class="value">{{ token.suffix }}</span>
......
......@@ -216,10 +216,10 @@ export default {
<template>
<div>
<loading-icon
class="loading-animation prepend-top-20"
size="2"
v-if="isLoading"
:label="s__('GroupsTree|Loading groups')"
class="loading-animation prepend-top-20"
size="2"
/>
<groups-component
v-if="!isLoading"
......@@ -230,10 +230,10 @@ export default {
/>
<deprecated-modal
v-show="showModal"
kind="warning"
:primary-button-label="__('Leave')"
:title="__('Are you sure?')"
:text="groupLeaveConfirmationMessage"
kind="warning"
@cancel="hideLeaveGroupModal"
@submit="leaveGroup"
/>
......
......@@ -71,14 +71,14 @@ export default {
<template>
<li
@click.stop="onClickRowGroup"
:id="groupDomId"
:class="rowClass"
class="group-row"
@click.stop="onClickRowGroup"
>
<div
class="group-row-contents"
:class="{ 'project-row-contents': !isGroup }">
:class="{ 'project-row-contents': !isGroup }"
class="group-row-contents">
<item-actions
v-if="isGroup"
:group="group"
......@@ -99,8 +99,8 @@ export default {
/>
</div>
<div
class="avatar-container prepend-top-8 prepend-left-5 s24 d-none d-sm-block"
:class="{ 'content-loading': group.isChildrenLoading }"
class="avatar-container prepend-top-8 prepend-left-5 s24 d-none d-sm-block"
>
<a
:href="group.relativePath"
......@@ -108,14 +108,14 @@ export default {
>
<img
v-if="hasAvatar"
class="avatar s24"
:src="group.avatarUrl"
class="avatar s24"
/>
<identicon
v-else
size-class="s24"
:entity-id="group.id"
:entity-name="group.name"
size-class="s24"
/>
</a>
</div>
......
......@@ -54,13 +54,13 @@ export default {
<a
v-tooltip
v-if="group.canLeave"
@click.prevent="onLeaveGroup"
:href="group.leavePath"
:title="leaveBtnTitle"
:aria-label="leaveBtnTitle"
data-container="body"
data-placement="bottom"
class="leave-group btn no-expand">
class="leave-group btn no-expand"
@click.prevent="onLeaveGroup">
<icon name="leave"/>
</a>
</div>
......
......@@ -45,44 +45,44 @@
<div class="stats">
<item-stats-value
v-if="isGroup"
css-class="number-subgroups"
icon-name="folder"
:title="__('Subgroups')"
:value="item.subgroupCount"
css-class="number-subgroups"
icon-name="folder"
/>
<item-stats-value
v-if="isGroup"
css-class="number-projects"
icon-name="bookmark"
:title="__('Projects')"
:value="item.projectCount"
css-class="number-projects"
icon-name="bookmark"
/>
<item-stats-value
v-if="isGroup"
css-class="number-users"
icon-name="users"
:title="__('Members')"
:value="item.memberCount"
css-class="number-users"
icon-name="users"
/>
<item-stats-value
v-if="isProject"
:value="item.starCount"
css-class="project-stars"
icon-name="star"
:value="item.starCount"
/>
<item-stats-value
css-class="item-visibility"
tooltip-placement="left"
:icon-name="visibilityIcon"
:title="visibilityTooltip"
css-class="item-visibility"
tooltip-placement="left"
/>
<div
class="last-updated"
v-if="isProject"
class="last-updated"
>
<time-ago-tooltip
tooltip-placement="bottom"
:time="item.updatedAt"
tooltip-placement="bottom"
/>
</div>
</div>
......
......@@ -52,10 +52,10 @@
<template>
<span
v-tooltip
data-container="body"
:data-placement="tooltipPlacement"
:class="cssClass"
:title="title"
data-container="body"
>
<icon :name="iconName" />
<span
......
......@@ -39,12 +39,12 @@ export default {
<li v-once>
<a
v-tooltip
data-container="body"
data-placement="right"
:href="goBackUrl"
class="ide-sidebar-link"
:title="s__('IDE|Go back')"
:aria-label="s__('IDE|Go back')"
data-container="body"
data-placement="right"
class="ide-sidebar-link"
>
<icon
:size="16"
......@@ -55,16 +55,16 @@ export default {
<li>
<button
v-tooltip
data-container="body"
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-edit-mode"
:class="{
active: currentActivityView === $options.activityBarViews.edit
}"
@click.prevent="changedActivityView($event, $options.activityBarViews.edit)"
:title="s__('IDE|Edit')"
:aria-label="s__('IDE|Edit')"
data-container="body"
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-edit-mode"
@click.prevent="changedActivityView($event, $options.activityBarViews.edit)"
>
<icon
name="code"
......@@ -74,16 +74,16 @@ export default {
<li>
<button
v-tooltip
data-container="body"
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-review-mode"
:class="{
active: currentActivityView === $options.activityBarViews.review
}"
@click.prevent="changedActivityView($event, $options.activityBarViews.review)"
:title="s__('IDE|Review')"
:aria-label="s__('IDE|Review')"
data-container="body"
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-review-mode"
@click.prevent="changedActivityView($event, $options.activityBarViews.review)"
>
<icon
name="file-modified"
......@@ -93,16 +93,16 @@ export default {
<li v-show="hasChanges">
<button
v-tooltip
data-container="body"
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-commit-mode"
:class="{
active: currentActivityView === $options.activityBarViews.commit
}"
@click.prevent="changedActivityView($event, $options.activityBarViews.commit)"
:title="s__('IDE|Commit')"
:aria-label="s__('IDE|Commit')"
data-container="body"
data-placement="right"
type="button"
class="ide-sidebar-link js-ide-commit-mode"
@click.prevent="changedActivityView($event, $options.activityBarViews.commit)"
>
<icon
name="commit"
......
......@@ -91,7 +91,6 @@ export default {
<template>
<div
class="multi-file-commit-form"
:class="{
'is-compact': isCompact,
'is-full': !isCompact
......@@ -99,6 +98,7 @@ export default {
:style="{
height: componentHeight ? `${componentHeight}px` : null,
}"
class="multi-file-commit-form"
>
<transition
name="commit-form-slide-up"
......@@ -108,12 +108,12 @@ export default {
>
<div
v-if="isCompact"
class="commit-form-compact"
ref="compactEl"
class="commit-form-compact"
>
<button
type="button"
:disabled="!hasChanges"
type="button"
class="btn btn-primary btn-sm btn-block"
@click="toggleIsSmall"
>
......@@ -126,8 +126,8 @@ export default {
</div>
<form
v-if="!isCompact"
@submit.prevent.stop="commitChanges"
ref="formEl"
@submit.prevent.stop="commitChanges"
>
<transition name="fade">
<success-message
......@@ -143,8 +143,8 @@ export default {
<loading-button
:loading="submitCommitLoading"
:disabled="commitButtonDisabled"
container-class="btn btn-success btn-sm float-left"
:label="__('Commit')"
container-class="btn btn-success btn-sm float-left"
@click="commitChanges"
/>
<button
......
......@@ -43,6 +43,15 @@ export default {
required: false,
default: false,
},
activeFileKey: {
type: String,
required: false,
default: null,
},
keyPrefix: {
type: String,
required: true,
},
},
data() {
return {
......@@ -113,8 +122,9 @@ export default {
<list-item
:file="file"
:action-component="itemActionComponent"
:key-prefix="title"
:key-prefix="keyPrefix"
:staged-list="stagedList"
:active-file-key="activeFileKey"
/>
</li>
</ul>
......
......@@ -30,6 +30,11 @@ export default {
required: false,
default: false,
},
activeFileKey: {
type: String,
required: false,
default: null,
},
},
computed: {
iconName() {
......@@ -39,6 +44,12 @@ export default {
iconClass() {
return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
},
fullKey() {
return `${this.keyPrefix}-${this.file.key}`;
},
isActive() {
return this.activeFileKey === this.fullKey;
},
},
methods: {
...mapActions([
......@@ -51,7 +62,7 @@ export default {
openFileInEditor() {
return this.openPendingTab({
file: this.file,
keyPrefix: this.keyPrefix.toLowerCase(),
keyPrefix: this.keyPrefix,
}).then(changeViewer => {
if (changeViewer) {
this.updateViewer(viewerTypes.diff);
......@@ -70,7 +81,12 @@ export default {
</script>
<template>
<div class="multi-file-commit-list-item">
<div
:class="{
'is-active': isActive
}"
class="multi-file-commit-list-item"
>
<button
type="button"
class="multi-file-commit-list-path"
......
......@@ -66,10 +66,10 @@ export default {
<template>
<fieldset class="common-note-form ide-commit-message-field">
<div
class="md-area"
:class="{
'is-focused': isFocused
}"
class="md-area"
>
<div
v-once
......@@ -92,10 +92,10 @@ export default {
<div class="ide-commit-message-textarea-container">
<div class="ide-commit-message-highlights-container">
<div
class="note-textarea highlights monospace"
:style="{
transform: `translate3d(0, ${-scrollTop}px, 0)`
}"
class="note-textarea highlights monospace"
>
<div
v-for="(line, index) in allLines"
......@@ -113,15 +113,15 @@ export default {
</div>
</div>
<textarea
class="note-textarea ide-commit-message-textarea"
name="commit-message"
ref="textarea"
:placeholder="__('Write a commit message...')"
:value="text"
class="note-textarea ide-commit-message-textarea"
name="commit-message"
@scroll="handleScroll"
@input="onInput"
@focus="updateIsFocused(true)"
@blur="updateIsFocused(false)"
ref="textarea"
>
</textarea>
</div>
......
......@@ -58,12 +58,12 @@ export default {
}"
>
<input
type="radio"
name="commit-action"
:value="value"
@change="updateCommitAction($event.target.value)"
:checked="commitAction === value"
:disabled="disabled"
type="radio"
name="commit-action"
@change="updateCommitAction($event.target.value)"
/>
<span class="prepend-left-10">
<span
......@@ -80,9 +80,9 @@ export default {
class="ide-commit-new-branch"
>
<input
:placeholder="newBranchName"
type="text"
class="form-control monospace"
:placeholder="newBranchName"
@input="updateBranchName($event.target.value)"
/>
</div>
......
......@@ -29,30 +29,30 @@ export default {
>
<button
v-tooltip
type="button"
class="btn btn-blank append-right-5"
:aria-label="__('Stage changes')"
:title="__('Stage changes')"
type="button"
class="btn btn-blank append-right-5"
data-container="body"
@click.stop="stageChange(path)"
>
<icon
name="mobile-issue-close"
:size="12"
name="mobile-issue-close"
/>
</button>
<button
v-tooltip
type="button"
class="btn btn-blank"
:aria-label="__('Discard changes')"
:title="__('Discard changes')"
type="button"
class="btn btn-blank"
data-container="body"
@click.stop="discardFileChanges(path)"
>
<icon
name="remove"
:size="12"
name="remove"
/>
</button>
</div>
......
......@@ -29,16 +29,16 @@ export default {
>
<button
v-tooltip
type="button"
class="btn btn-blank"
:aria-label="__('Unstage changes')"
:title="__('Unstage changes')"
type="button"
class="btn btn-blank"
data-container="body"
@click="unstageChange(path)"
>
<icon
name="history"
:size="12"
name="history"
/>
</button>
</div>
......
......@@ -44,11 +44,11 @@ export default {
<ul>
<li>
<a
href="#"
@click.prevent="changeMode($options.viewerTypes.mr)"
:class="{
'is-active': viewer === $options.viewerTypes.mr,
}"
href="#"
@click.prevent="changeMode($options.viewerTypes.mr)"
>
<strong class="dropdown-menu-inner-title">
{{ mergeReviewLine }}
......@@ -60,11 +60,11 @@ export default {
</li>
<li>
<a
href="#"
@click.prevent="changeMode($options.viewerTypes.diff)"
:class="{
'is-active': viewer === $options.viewerTypes.diff,
}"
href="#"
@click.prevent="changeMode($options.viewerTypes.diff)"
>
<strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong>
<span class="dropdown-menu-inner-content">
......
......@@ -26,15 +26,15 @@ export default {
>
<a
:href="file.permalink"
target="_blank"
:title="s__('IDE|Open in file view')"
target="_blank"
rel="noopener noreferrer"
>
<span class="vertical-align-middle">Open in file view</span>
<icon
:size="16"
name="external-link"
css-classes="vertical-align-middle space-right"
:size="16"
/>
</a>
</div>
......
......@@ -173,38 +173,38 @@ export default {
>
<div class="dropdown-input">
<input
ref="searchInput"
:placeholder="__('Search files')"
v-model="searchText"
type="search"
class="dropdown-input-field"
:placeholder="__('Search files')"
autocomplete="off"
v-model="searchText"
ref="searchInput"
@keydown="onKeydown($event)"
@keyup="onKeyup($event)"
/>
<i
aria-hidden="true"
class="fa fa-search dropdown-input-search"
:class="{
hidden: showClearInputButton
}"
aria-hidden="true"
class="fa fa-search dropdown-input-search"
></i>
<i
role="button"
:aria-label="__('Clear search input')"
class="fa fa-times dropdown-input-clear"
:class="{
show: showClearInputButton
}"
role="button"
class="fa fa-times dropdown-input-clear"
@click="clearSearchInput"
></i>
</div>
<div>
<virtual-list
ref="virtualScrollList"
:size="listHeight"
:remain="listShowCount"
wtag="ul"
ref="virtualScrollList"
>
<template v-if="filteredBlobsLength">
<li
......@@ -212,11 +212,11 @@ export default {
:key="file.key"
>
<item
class="disable-hover"
:file="file"
:search-text="searchText"
:focused="index === focusedIndex"
:index="index"
class="disable-hover"
@click="openFile"
@mouseover="onMouseOver"
@mousemove="onMouseMove"
......
......@@ -59,11 +59,11 @@ export default {
<template>
<button
type="button"
class="diff-changed-file"
:class="{
'is-focused': focused,
}"
type="button"
class="diff-changed-file"
@click.prevent="clickRow"
@mouseover="mouseOverRow"
@mousemove="mouseMove"
......
......@@ -93,8 +93,8 @@ export default {
:merge-request-id="currentMergeRequestId"
/>
<repo-editor
class="multi-file-edit-pane-content"
:file="activeFile"
class="multi-file-edit-pane-content"
/>
</template>
<template
......
......@@ -36,8 +36,8 @@ export default {
<template>
<ide-tree-list
:viewer-type="viewer"
header-class="ide-review-header"
:disable-action-dropdown="true"
header-class="ide-review-header"
>
<template
slot="header"
......
......@@ -115,17 +115,17 @@ export default {
<div class="multi-file-commit-panel-inner">
<template v-if="loading">
<div
class="multi-file-loading-container"
v-for="n in 3"
:key="n"
class="multi-file-loading-container"
>
<skeleton-loading-container />
</div>
</template>
<template v-else>
<div
class="context-header ide-context-header dropdown"
ref="mergeRequestDropdown"
class="context-header ide-context-header dropdown"
>
<button
type="button"
......@@ -136,18 +136,18 @@ export default {
class="avatar-container s40 project-avatar"
>
<project-avatar-image
class="avatar-container project-avatar"
:link-href="currentProject.path"
:img-src="currentProject.avatar_url"
:img-alt="currentProject.name"
:img-size="40"
class="avatar-container project-avatar"
/>
</div>
<identicon
v-else
size-class="s40"
:entity-id="currentProject.id"
:entity-name="currentProject.name"
size-class="s40"
/>
<div class="ide-sidebar-project-title">
<div class="sidebar-context-title">
......@@ -155,11 +155,11 @@ export default {
</div>
<div class="d-flex">
<div
v-tooltip
v-if="currentBranchId"
class="sidebar-context-title ide-sidebar-branch-title"
ref="branchId"
v-tooltip
:title="branchTooltipTitle"
class="sidebar-context-title ide-sidebar-branch-title"
>
<icon
name="branch"
......@@ -168,10 +168,10 @@ export default {
</div>
<div
v-if="currentMergeRequestId"
class="sidebar-context-title ide-sidebar-branch-title"
:class="{
'prepend-left-8': currentBranchId
}"
class="sidebar-context-title ide-sidebar-branch-title"
>
<icon
name="git-merge"
......
......@@ -75,22 +75,22 @@ export default {
<template>
<footer class="ide-status-bar">
<div
class="ide-status-branch"
v-if="lastCommit && lastCommitFormatedAge"
class="ide-status-branch"
>
<span
class="ide-status-pipeline"
v-if="latestPipeline && latestPipeline.details"
class="ide-status-pipeline"
>
<ci-icon
:status="latestPipeline.details.status"
v-tooltip
:status="latestPipeline.details.status"
:title="latestPipeline.details.status.text"
/>
Pipeline
<a
class="monospace"
:href="latestPipeline.details.status.details_path">#{{ latestPipeline.id }}</a>
:href="latestPipeline.details.status.details_path"
class="monospace">#{{ latestPipeline.id }}</a>
{{ latestPipeline.details.status.text }}
for
</span>
......@@ -100,18 +100,18 @@ export default {
/>
<a
v-tooltip
class="commit-sha"
:title="lastCommit.message"
:href="getCommitPath(lastCommit.short_id)"
class="commit-sha"
>{{ lastCommit.short_id }}</a>
by
{{ lastCommit.author_name }}
<time
v-tooltip
data-placement="top"
data-container="body"
:datetime="lastCommit.committed_date"
:title="tooltipTitle(lastCommit.committed_date)"
data-placement="top"
data-container="body"
>
{{ lastCommitFormatedAge }}
</time>
......@@ -129,8 +129,8 @@ export default {
{{ file.eol }}
</div>
<div
class="ide-status-file"
v-if="file && !file.binary">
v-if="file && !file.binary"
class="ide-status-file">
{{ file.editorRow }}:{{ file.editorColumn }}
</div>
<div
......
......@@ -50,17 +50,17 @@ export default {
>
<template v-if="showLoading">
<div
class="multi-file-loading-container"
v-for="n in 3"
:key="n"
class="multi-file-loading-container"
>
<skeleton-loading-container />
</div>
</template>
<template v-else>
<header
class="ide-tree-header"
:class="headerClass"
class="ide-tree-header"
>
<slot name="header"></slot>
</header>
......
......@@ -93,10 +93,10 @@ export default {
<a
v-tooltip
:title="__('Show complete raw log')"
:href="detailJob.rawPath"
data-placement="top"
data-container="body"
class="controllers-buttons"
:href="detailJob.rawPath"
target="_blank"
>
<i
......@@ -105,23 +105,24 @@ export default {
></i>
</a>
<scroll-button
direction="up"
:disabled="isScrolledToTop"
direction="up"
@click="scrollUp"
/>
<scroll-button
direction="down"
:disabled="isScrolledToBottom"
direction="down"
@click="scrollDown"
/>
</div>
</div>
<pre
class="build-trace mb-0 h-100"
ref="buildTrace"
class="build-trace mb-0 h-100"
@scroll="scrollBuildLog"
>
<code
v-show="!detailJob.isLoading"
class="bash"
v-html="jobOutput"
>
......
......@@ -24,10 +24,10 @@ export default {
<template>
<div class="d-flex align-items-center">
<ci-icon
class="d-flex"
:status="job.status"
:borderless="true"
:size="24"
class="d-flex"
/>
<span class="prepend-left-8">
{{ job.name }}
......@@ -38,8 +38,8 @@ export default {
>
{{ jobId }}
<icon
name="external-link"
:size="12"
name="external-link"
/>
</a>
</span>
......
......@@ -47,15 +47,15 @@ export default {
<template>
<div
v-tooltip
:title="tooltipTitle"
class="controllers-buttons"
data-container="body"
data-placement="top"
:title="tooltipTitle"
>
<button
:disabled="disabled"
class="btn-scroll btn-transparent btn-blank"
type="button"
:disabled="disabled"
@click="clickedScroll"
>
<icon
......
......@@ -27,8 +27,8 @@ export default {
<template>
<div class="ide-job-item">
<job-description
class="append-right-default"
:job="job"
class="append-right-default"
/>
<div class="ml-auto align-self-center">
<button
......
......@@ -60,10 +60,10 @@ export default {
class="ide-stage card prepend-top-default"
>
<div
class="card-header"
:class="{
'border-bottom-0': stage.isCollapsed
}"
class="card-header"
@click="toggleCollapsed"
>
<ci-icon
......@@ -72,10 +72,10 @@ export default {
/>
<strong
v-tooltip="showTooltip"
ref="stageTitle"
:title="showTooltip ? stage.name : null"
data-container="body"
class="prepend-left-8 ide-stage-title"
ref="stageTitle"
>
{{ stage.name }}
</strong>
......@@ -93,8 +93,8 @@ export default {
/>
</div>
<div
class="card-body"
v-show="!stage.isCollapsed"
class="card-body"
>
<loading-icon
v-if="showLoadingIcon"
......
......@@ -42,8 +42,8 @@ export default {
</span>
</template>
<list
type="created"
:empty-text="__('You have not created any merge requests')"
type="created"
/>
</tab>
<tab>
......@@ -54,8 +54,8 @@ export default {
</span>
</template>
<list
type="assigned"
:empty-text="__('You do not have any assigned merge requests')"
type="assigned"
/>
</tab>
</tabs>
......
......@@ -47,8 +47,8 @@ export default {
<span class="d-flex append-right-default ide-merge-request-current-icon">
<icon
v-if="isActive"
name="mobile-issue-close"
:size="18"
name="mobile-issue-close"
/>
</span>
<span>
......
......@@ -80,12 +80,12 @@ export default {
<div>
<div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
<input
type="search"
class="dropdown-input-field"
ref="searchInput"
:placeholder="__('Search merge requests')"
v-model="search"
type="search"
class="dropdown-input-field"
@input="searchMergeRequests"
ref="searchInput"
/>
<i
aria-hidden="true"
......@@ -94,8 +94,8 @@ export default {
</div>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
<loading-icon
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
v-if="isLoading"
class="mt-3 mb-3 align-self-center ml-auto mr-auto"
size="2"
/>
<ul
......
......@@ -14,10 +14,10 @@ export default {
<template>
<icon
name="git-merge"
v-tooltip
:title="__('Part of merge request changes')"
css-classes="append-right-8"
:size="12"
name="git-merge"
css-classes="append-right-8"
/>
</template>
......@@ -55,10 +55,10 @@ export default {
<template>
<div class="ide-new-btn">
<div
class="dropdown"
:class="{
show: dropdownOpen,
}"
class="dropdown"
>
<button
type="button"
......@@ -67,19 +67,19 @@ export default {
@click.stop="openDropdown()"
>
<icon
name="plus"
:size="12"
name="plus"
css-classes="float-left"
/>
<icon
name="arrow-down"
:size="12"
name="arrow-down"
css-classes="float-left"
/>
</button>
<ul
class="dropdown-menu dropdown-menu-right"
ref="dropdownMenu"
class="dropdown-menu dropdown-menu-right"
>
<li>
<a
......
......@@ -71,18 +71,18 @@ export default {
>
<form
slot="body"
@submit.prevent="createEntryInStore"
class="form-group row"
@submit.prevent="createEntryInStore"
>
<label class="label-light col-form-label col-sm-3">
{{ __('Name') }}
</label>
<div class="col-sm-9">
<input
ref="fieldName"
v-model="entryName"
type="text"
class="form-control"
v-model="entryName"
ref="fieldName"
/>
</div>
</form>
......
......@@ -67,9 +67,9 @@
</a>
<input
id="file-upload"
ref="fileUpload"
type="file"
class="hidden"
ref="fileUpload"
/>
</div>
</template>
......@@ -44,10 +44,10 @@ export default {
>
<resizable-panel
v-if="rightPane"
class="multi-file-commit-panel-inner"
:collapsible="false"
:initial-width="350"
:min-size="350"
class="multi-file-commit-panel-inner"
side="right"
>
<component :is="rightPane" />
......@@ -57,13 +57,13 @@ export default {
<li>
<button
v-tooltip
data-container="body"
data-placement="left"
:title="__('Pipelines')"
class="ide-sidebar-link is-right"
:class="{
active: pipelinesActive
}"
data-container="body"
data-placement="left"
class="ide-sidebar-link is-right"
type="button"
@click="clickTab($event, $options.rightSidebarViews.pipelines)"
>
......
......@@ -75,8 +75,8 @@ export default {
>
#{{ latestPipeline.id }}
<icon
name="external-link"
:size="12"
name="external-link"
/>
</a>
</span>
......@@ -94,7 +94,7 @@ export default {
<p class="append-bottom-0">
{{ __('Found errors in your .gitlab-ci.yml:') }}
</p>
<p class="append-bottom-0">
<p class="append-bottom-0 break-word">
{{ latestPipeline.yamlError }}
</p>
<p
......
......@@ -6,7 +6,7 @@ import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import CommitFilesList from './commit_sidebar/list.vue';
import EmptyState from './commit_sidebar/empty_state.vue';
import * as consts from '../stores/modules/commit/constants';
import { activityBarViews } from '../constants';
import { activityBarViews, stageKeys } from '../constants';
export default {
components: {
......@@ -27,11 +27,14 @@ export default {
'unusedSeal',
]),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges']),
...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges', 'activeFile']),
...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']),
showStageUnstageArea() {
return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal);
},
activeFileKey() {
return this.activeFile ? this.activeFile.key : null;
},
},
watch: {
hasChanges() {
......@@ -44,6 +47,7 @@ export default {
if (this.lastOpenedFile) {
this.openPendingTab({
file: this.lastOpenedFile,
keyPrefix: this.lastOpenedFile.changed ? stageKeys.unstaged : stageKeys.staged,
})
.then(changeViewer => {
if (changeViewer) {
......@@ -62,6 +66,7 @@ export default {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commitChanges());
},
},
stageKeys,
};
</script>
......@@ -72,8 +77,8 @@ export default {
<deprecated-modal
id="ide-create-branch-modal"
:primary-button-label="__('Create new branch')"
kind="success"
:title="__('Branch has changed')"
kind="success"
@submit="forceCreateNewBranch"
>
<template slot="body">
......@@ -85,22 +90,26 @@ export default {
v-if="showStageUnstageArea"
>
<commit-files-list
class="is-first"
icon-name="unstaged"
:title="__('Unstaged')"
:key-prefix="$options.stageKeys.unstaged"
:file-list="changedFiles"
action="stageAllChanges"
:action-btn-text="__('Stage all')"
:active-file-key="activeFileKey"
class="is-first"
icon-name="unstaged"
action="stageAllChanges"
item-action-component="stage-button"
/>
<commit-files-list
icon-name="staged"
:title="__('Staged')"
:key-prefix="$options.stageKeys.staged"
:file-list="stagedFiles"
action="unstageAllChanges"
:action-btn-text="__('Unstage all')"
item-action-component="unstage-button"
:staged-list="true"
:active-file-key="activeFileKey"
icon-name="staged"
action="unstageAllChanges"
item-action-component="unstage-button"
/>
</template>
<empty-state
......
......@@ -2,6 +2,7 @@
import { mapState, mapGetters, mapActions } from 'vuex';
import flash from '~/flash';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import { activityBarViews, viewerTypes } from '../constants';
import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
......@@ -9,6 +10,7 @@ import ExternalLink from './external_link.vue';
export default {
components: {
ContentViewer,
DiffViewer,
ExternalLink,
},
props: {
......@@ -18,7 +20,13 @@ export default {
},
},
computed: {
...mapState(['rightPanelCollapsed', 'viewer', 'panelResizing', 'currentActivityView']),
...mapState([
'rightPanelCollapsed',
'viewer',
'panelResizing',
'currentActivityView',
'rightPane',
]),
...mapGetters([
'currentMergeRequest',
'getStagedFile',
......@@ -29,9 +37,18 @@ export default {
shouldHideEditor() {
return this.file && this.file.binary && !this.file.content;
},
showContentViewer() {
return (
(this.shouldHideEditor || this.file.viewMode === 'preview') &&
(this.viewer !== viewerTypes.mr || !this.file.mrChange)
);
},
showDiffViewer() {
return this.shouldHideEditor && this.file.mrChange && this.viewer === viewerTypes.mr;
},
editTabCSS() {
return {
active: this.file.viewMode === 'edit',
active: this.file.viewMode === 'editor',
};
},
previewTabCSS() {
......@@ -53,7 +70,7 @@ export default {
if (this.currentActivityView !== activityBarViews.edit) {
this.setFileViewMode({
file: this.file,
viewMode: 'edit',
viewMode: 'editor',
});
}
}
......@@ -62,7 +79,7 @@ export default {
if (this.currentActivityView !== activityBarViews.edit) {
this.setFileViewMode({
file: this.file,
viewMode: 'edit',
viewMode: 'editor',
});
}
},
......@@ -77,6 +94,9 @@ export default {
this.editor.updateDimensions();
}
},
rightPane() {
this.editor.updateDimensions();
},
},
beforeDestroy() {
this.editor.dispose();
......@@ -190,14 +210,14 @@ export default {
>
<div class="ide-mode-tabs clearfix" >
<ul
class="nav-links float-left"
v-if="!shouldHideEditor && isEditModeActive"
class="nav-links float-left"
>
<li :class="editTabCSS">
<a
href="javascript:void(0);"
role="button"
@click.prevent="setFileViewMode({ file, viewMode: 'edit' })">
@click.prevent="setFileViewMode({ file, viewMode: 'editor' })">
<template v-if="viewer === $options.viewerTypes.edit">
{{ __('Edit') }}
</template>
......@@ -222,19 +242,27 @@ export default {
/>
</div>
<div
v-show="!shouldHideEditor && file.viewMode === 'edit'"
v-show="!shouldHideEditor && file.viewMode ==='editor'"
ref="editor"
class="multi-file-editor-holder"
:class="{
'is-readonly': isCommitModeActive,
}"
class="multi-file-editor-holder"
>
</div>
<content-viewer
v-if="shouldHideEditor || file.viewMode === 'preview'"
v-if="showContentViewer"
:content="file.content || file.raw"
:path="file.rawPath || file.path"
:file-size="file.size"
:project-path="file.projectId"/>
<diff-viewer
v-if="showDiffViewer"
:diff-mode="file.mrChange.diffMode"
:new-path="file.mrChange.new_path"
:new-sha="currentMergeRequest.sha"
:old-path="file.mrChange.old_path"
:old-sha="currentMergeRequest.baseCommitSha"
:project-path="file.projectId"/>
</div>
</template>
......@@ -120,17 +120,17 @@ export default {
<template>
<div>
<div
class="file"
:class="fileClass"
@click="clickFile"
class="file"
role="button"
@click="clickFile"
>
<div
class="file-name"
>
<span
class="ide-file-name str-truncated"
:style="levelIndentation"
class="ide-file-name str-truncated"
>
<file-icon
:file-name="file.name"
......@@ -156,10 +156,10 @@ export default {
<icon
v-tooltip
:title="folderChangesTooltip"
:size="12"
data-container="body"
data-placement="right"
name="file-modified"
:size="12"
css-classes="prepend-left-5 multi-file-modified"
/>
</span>
......
......@@ -26,8 +26,8 @@ export default {
<template>
<span
v-if="file.file_lock"
v-tooltip
v-if="file.file_lock"
:title="lockTooltip"
data-container="body"
>
......
......@@ -33,8 +33,8 @@
<td class="d-none d-sm-block">
<skeleton-loading-container
class="animation-container-right"
:small="true"
class="animation-container-right"
/>
</td>
</template>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment