Commit f4023a69 authored by Jan Provaznik's avatar Jan Provaznik

Merge remote-tracking branch 'origin/master' into dev-master

parents 79911cb4 a0ca86e5
Dangerfile gitlab-language=ruby Dangerfile gitlab-language=ruby
db/schema.rb merge=merge_db_schema
...@@ -694,7 +694,10 @@ gitlab:setup-mysql: ...@@ -694,7 +694,10 @@ gitlab:setup-mysql:
# Frontend-related jobs # Frontend-related jobs
gitlab:assets:compile: gitlab:assets:compile:
<<: *dedicated-no-docs-and-no-qa-pull-cache-job <<: *dedicated-no-docs-and-no-qa-pull-cache-job
image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-git-2.18-chrome-69.0-node-8.x-yarn-1.2-graphicsmagick-1.3.29-docker-18.06.1
dependencies: [] dependencies: []
services:
- docker:stable-dind
variables: variables:
NODE_ENV: "production" NODE_ENV: "production"
RAILS_ENV: "production" RAILS_ENV: "production"
...@@ -703,18 +706,23 @@ gitlab:assets:compile: ...@@ -703,18 +706,23 @@ gitlab:assets:compile:
WEBPACK_REPORT: "true" WEBPACK_REPORT: "true"
# we override the max_old_space_size to prevent OOM errors # we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584 NODE_OPTIONS: --max_old_space_size=3584
DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375
script: script:
- date - date
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache - yarn install --frozen-lockfile --production --cache-folder .yarn-cache
- date - date
- free -m - free -m
- bundle exec rake gitlab:assets:compile - bundle exec rake gitlab:assets:compile
- scripts/build_assets_image
artifacts: artifacts:
name: webpack-report name: webpack-report
expire_in: 31d expire_in: 31d
paths: paths:
- webpack-report/ - webpack-report/
- public/assets/ - public/assets/
tags:
- docker
karma: karma:
<<: *dedicated-no-docs-pull-cache-job <<: *dedicated-no-docs-pull-cache-job
......
# Simple container to store assets for later use
FROM scratch
ADD public/assets /assets/
CMD /bin/true
...@@ -13,7 +13,7 @@ export default () => { ...@@ -13,7 +13,7 @@ export default () => {
if (editBlobForm.length) { if (editBlobForm.length) {
const urlRoot = editBlobForm.data('relativeUrlRoot'); const urlRoot = editBlobForm.data('relativeUrlRoot');
const assetsPath = editBlobForm.data('assetsPrefix'); const assetsPath = editBlobForm.data('assetsPrefix');
const filePath = editBlobForm.data('blobFilename') const filePath = editBlobForm.data('blobFilename');
const currentAction = $('.js-file-title').data('currentAction'); const currentAction = $('.js-file-title').data('currentAction');
const projectId = editBlobForm.data('project-id'); const projectId = editBlobForm.data('project-id');
......
...@@ -42,7 +42,7 @@ export default Vue.extend({ ...@@ -42,7 +42,7 @@ export default Vue.extend({
required: true, required: true,
}, },
}, },
data () { data() {
return { return {
detailIssue: boardsStore.detail, detailIssue: boardsStore.detail,
filter: boardsStore.filter, filter: boardsStore.filter,
...@@ -55,27 +55,26 @@ export default Vue.extend({ ...@@ -55,27 +55,26 @@ export default Vue.extend({
}, },
isNewIssueShown() { isNewIssueShown() {
return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed'); return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed');
} },
}, },
watch: { watch: {
filter: { filter: {
handler() { handler() {
this.list.page = 1; this.list.page = 1;
this.list.getIssues(true) this.list.getIssues(true).catch(() => {
.catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
}, },
deep: true, deep: true,
}
}, },
mounted () { },
mounted() {
this.sortableOptions = getBoardSortableDefaultOptions({ this.sortableOptions = getBoardSortableDefaultOptions({
disabled: this.disabled, disabled: this.disabled,
group: 'boards', group: 'boards',
draggable: '.is-draggable', draggable: '.is-draggable',
handle: '.js-board-handle', handle: '.js-board-handle',
onEnd: (e) => { onEnd: e => {
sortableEnd(); sortableEnd();
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) { if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
...@@ -86,14 +85,15 @@ export default Vue.extend({ ...@@ -86,14 +85,15 @@ export default Vue.extend({
boardsStore.moveList(list, order); boardsStore.moveList(list, order);
}); });
} }
} },
}); });
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions); this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
}, },
created() { created() {
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) { if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false'; const isCollapsed =
localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
this.list.isExpanded = !isCollapsed; this.list.isExpanded = !isCollapsed;
} }
...@@ -107,7 +107,10 @@ export default Vue.extend({ ...@@ -107,7 +107,10 @@ export default Vue.extend({
this.list.isExpanded = !this.list.isExpanded; this.list.isExpanded = !this.list.isExpanded;
if (AccessorUtilities.isLocalStorageAccessSafe()) { if (AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded); localStorage.setItem(
`boards.${this.boardId}.${this.list.type}.expanded`,
this.list.isExpanded,
);
} }
} }
}, },
......
...@@ -32,16 +32,16 @@ export default { ...@@ -32,16 +32,16 @@ export default {
boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position'); boardsStore.state.lists = _.sortBy(boardsStore.state.lists, 'position');
// Save the labels // Save the labels
gl.boardService.generateDefaultLists() gl.boardService
.generateDefaultLists()
.then(res => res.data) .then(res => res.data)
.then((data) => { .then(data => {
data.forEach((listObj) => { data.forEach(listObj => {
const list = boardsStore.findList('title', listObj.title); const list = boardsStore.findList('title', listObj.title);
list.id = listObj.id; list.id = listObj.id;
list.label.id = listObj.label.id; list.label.id = listObj.label.id;
list.getIssues() list.getIssues().catch(() => {
.catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
}); });
...@@ -57,7 +57,6 @@ export default { ...@@ -57,7 +57,6 @@ export default {
clearBlankState: boardsStore.removeBlankState.bind(boardsStore), clearBlankState: boardsStore.removeBlankState.bind(boardsStore),
}, },
}; };
</script> </script>
<template> <template>
......
<script> <script>
/* eslint-disable vue/require-default-prop */ /* eslint-disable vue/require-default-prop */
import IssueCardInner from './issue_card_inner.vue'; import IssueCardInner from './issue_card_inner.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
export default { export default {
name: 'BoardsIssueCard', name: 'BoardsIssueCard',
components: { components: {
IssueCardInner, IssueCardInner,
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
} }
}, },
}, },
}; };
</script> </script>
<template> <template>
......
...@@ -62,7 +62,8 @@ export default { ...@@ -62,7 +62,8 @@ export default {
eventHub.$emit(`scroll-board-list-${this.list.id}`); eventHub.$emit(`scroll-board-list-${this.list.id}`);
this.cancel(); this.cancel();
return this.list.newIssue(issue) return this.list
.newIssue(issue)
.then(() => { .then(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions // Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$refs.submitButton).enable(); $(this.$refs.submitButton).enable();
......
...@@ -38,7 +38,7 @@ export default Vue.extend({ ...@@ -38,7 +38,7 @@ export default Vue.extend({
}; };
}, },
computed: { computed: {
showSidebar () { showSidebar() {
return Object.keys(this.issue).length; return Object.keys(this.issue).length;
}, },
milestoneTitle() { milestoneTitle() {
...@@ -51,18 +51,20 @@ export default Vue.extend({ ...@@ -51,18 +51,20 @@ export default Vue.extend({
return this.issue.labels && this.issue.labels.length; return this.issue.labels && this.issue.labels.length;
}, },
labelDropdownTitle() { labelDropdownTitle() {
return this.hasLabels ? sprintf(__('%{firstLabel} +%{labelCount} more'), { return this.hasLabels
? sprintf(__('%{firstLabel} +%{labelCount} more'), {
firstLabel: this.issue.labels[0].title, firstLabel: this.issue.labels[0].title,
labelCount: this.issue.labels.length - 1 labelCount: this.issue.labels.length - 1,
}) : __('Label'); })
: __('Label');
}, },
selectedLabels() { selectedLabels() {
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : ''; return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
} },
}, },
watch: { watch: {
detail: { detail: {
handler () { handler() {
if (this.issue.id !== this.detail.issue.id) { if (this.issue.id !== this.detail.issue.id) {
$('.block.assignee') $('.block.assignee')
.find('input:not(.js-vue)[name="issue[assignee_ids][]"]') .find('input:not(.js-vue)[name="issue[assignee_ids][]"]')
...@@ -71,17 +73,19 @@ export default Vue.extend({ ...@@ -71,17 +73,19 @@ export default Vue.extend({
}); });
$('.js-issue-board-sidebar', this.$el).each((i, el) => { $('.js-issue-board-sidebar', this.$el).each((i, el) => {
$(el).data('glDropdown').clearMenu(); $(el)
.data('glDropdown')
.clearMenu();
}); });
} }
this.issue = this.detail.issue; this.issue = this.detail.issue;
this.list = this.detail.list; this.list = this.detail.list;
}, },
deep: true deep: true,
}, },
}, },
created () { created() {
// Get events from glDropdown // Get events from glDropdown
eventHub.$on('sidebar.removeAssignee', this.removeAssignee); eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
eventHub.$on('sidebar.addAssignee', this.addAssignee); eventHub.$on('sidebar.addAssignee', this.addAssignee);
...@@ -94,7 +98,7 @@ export default Vue.extend({ ...@@ -94,7 +98,7 @@ export default Vue.extend({
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees); eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees); eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
}, },
mounted () { mounted() {
new IssuableContext(this.currentUser); new IssuableContext(this.currentUser);
new MilestoneSelect(); new MilestoneSelect();
new DueDateSelectors(); new DueDateSelectors();
...@@ -102,29 +106,30 @@ export default Vue.extend({ ...@@ -102,29 +106,30 @@ export default Vue.extend({
new Sidebar(); new Sidebar();
}, },
methods: { methods: {
closeSidebar () { closeSidebar() {
this.detail.issue = {}; this.detail.issue = {};
}, },
assignSelf () { assignSelf() {
// Notify gl dropdown that we are now assigning to current user // Notify gl dropdown that we are now assigning to current user
this.$refs.assigneeBlock.dispatchEvent(new Event('assignYourself')); this.$refs.assigneeBlock.dispatchEvent(new Event('assignYourself'));
this.addAssignee(this.currentUser); this.addAssignee(this.currentUser);
this.saveAssignees(); this.saveAssignees();
}, },
removeAssignee (a) { removeAssignee(a) {
boardsStore.detail.issue.removeAssignee(a); boardsStore.detail.issue.removeAssignee(a);
}, },
addAssignee (a) { addAssignee(a) {
boardsStore.detail.issue.addAssignee(a); boardsStore.detail.issue.addAssignee(a);
}, },
removeAllAssignees () { removeAllAssignees() {
boardsStore.detail.issue.removeAllAssignees(); boardsStore.detail.issue.removeAllAssignees();
}, },
saveAssignees () { saveAssignees() {
this.loadingAssignees = true; this.loadingAssignees = true;
boardsStore.detail.issue.update() boardsStore.detail.issue
.update()
.then(() => { .then(() => {
this.loadingAssignees = false; this.loadingAssignees = false;
}) })
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
export default { export default {
components: { components: {
UserAvatarLink, UserAvatarLink,
Icon, Icon,
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
}; };
}, },
}, },
}; };
</script> </script>
<template> <template>
<div> <div>
......
...@@ -20,7 +20,7 @@ export default { ...@@ -20,7 +20,7 @@ export default {
computed: { computed: {
contents() { contents() {
const obj = { const obj = {
title: 'You haven\'t added any issues to your project yet', title: "You haven't added any issues to your project yet",
content: ` content: `
An issue can be a bug, a todo or a feature request that needs to be An issue can be a bug, a todo or a feature request that needs to be
discussed in a project. Besides, issues are searchable and filterable. discussed in a project. Besides, issues are searchable and filterable.
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
}; };
if (this.activeTab === 'selected') { if (this.activeTab === 'selected') {
obj.title = 'You haven\'t selected any issues yet'; obj.title = "You haven't selected any issues yet";
obj.content = ` obj.content = `
Go back to <strong>Open issues</strong> and select some issues Go back to <strong>Open issues</strong> and select some issues
to add to your board. to add to your board.
......
...@@ -42,19 +42,17 @@ export default { ...@@ -42,19 +42,17 @@ export default {
const req = this.buildUpdateRequest(list); const req = this.buildUpdateRequest(list);
// Post the data to the backend // Post the data to the backend
gl.boardService gl.boardService.bulkUpdate(issueIds, req).catch(() => {
.bulkUpdate(issueIds, req)
.catch(() => {
Flash(__('Failed to update issues, please try again.')); Flash(__('Failed to update issues, please try again.'));
selectedIssues.forEach((issue) => { selectedIssues.forEach(issue => {
list.removeIssue(issue); list.removeIssue(issue);
list.issuesSize -= 1; list.issuesSize -= 1;
}); });
}); });
// Add the issues on the frontend // Add the issues on the frontend
selectedIssues.forEach((issue) => { selectedIssues.forEach(issue => {
list.addIssue(issue); list.addIssue(issue);
list.issuesSize += 1; list.issuesSize += 1;
}); });
......
<script> <script>
import ModalFilters from './filters'; import ModalFilters from './filters';
import ModalTabs from './tabs.vue'; import ModalTabs from './tabs.vue';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins'; import modalMixin from '../../mixins/modal_mixins';
export default { export default {
components: { components: {
ModalTabs, ModalTabs,
ModalFilters, ModalFilters,
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
ModalStore.toggleAll(); ModalStore.toggleAll();
}, },
}, },
}; };
</script> </script>
<template> <template>
<div> <div>
......
<script> <script>
/* global ListIssue */ /* global ListIssue */
import { urlParamsToObject } from '~/lib/utils/common_utils'; import { urlParamsToObject } from '~/lib/utils/common_utils';
import ModalHeader from './header.vue'; import ModalHeader from './header.vue';
import ModalList from './list.vue'; import ModalList from './list.vue';
import ModalFooter from './footer.vue'; import ModalFooter from './footer.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
export default { export default {
components: { components: {
EmptyState, EmptyState,
ModalHeader, ModalHeader,
...@@ -107,7 +107,8 @@ ...@@ -107,7 +107,8 @@
loadIssues(clearIssues = false) { loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false; if (!this.showAddIssuesModal) return false;
return gl.boardService.getBacklog({ return gl.boardService
.getBacklog({
...urlParamsToObject(this.filter.path), ...urlParamsToObject(this.filter.path),
page: this.page, page: this.page,
per: this.perPage, per: this.perPage,
...@@ -137,7 +138,7 @@ ...@@ -137,7 +138,7 @@
}); });
}, },
}, },
}; };
</script> </script>
<template> <template>
<div <div
......
<script> <script>
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import bp from '../../../breakpoints'; import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
import IssueCardInner from '../issue_card_inner.vue'; import IssueCardInner from '../issue_card_inner.vue';
export default { export default {
components: { components: {
IssueCardInner, IssueCardInner,
Icon, Icon,
...@@ -114,7 +114,7 @@ ...@@ -114,7 +114,7 @@
} }
}, },
}, },
}; };
</script> </script>
<template> <template>
<section <section
......
<script> <script>
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins'; import modalMixin from '../../mixins/modal_mixins';
export default { export default {
mixins: [modalMixin], mixins: [modalMixin],
data() { data() {
return ModalStore.store; return ModalStore.store;
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
destroyed() { destroyed() {
this.activeTab = 'all'; this.activeTab = 'all';
}, },
}; };
</script> </script>
<template> <template>
<div class="top-area prepend-top-10 append-bottom-10"> <div class="top-area prepend-top-10 append-bottom-10">
......
...@@ -6,7 +6,9 @@ import _ from 'underscore'; ...@@ -6,7 +6,9 @@ import _ from 'underscore';
import CreateLabelDropdown from '../../create_label'; import CreateLabelDropdown from '../../create_label';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
$(document).off('created.label').on('created.label', (e, label) => { $(document)
.off('created.label')
.on('created.label', (e, label) => {
boardsStore.new({ boardsStore.new({
title: label.title, title: label.title,
position: boardsStore.state.lists.length - 2, position: boardsStore.state.lists.length - 2,
...@@ -17,25 +19,28 @@ $(document).off('created.label').on('created.label', (e, label) => { ...@@ -17,25 +19,28 @@ $(document).off('created.label').on('created.label', (e, label) => {
color: label.color, color: label.color,
}, },
}); });
}); });
export default function initNewListDropdown() { export default function initNewListDropdown() {
$('.js-new-board-list').each(function () { $('.js-new-board-list').each(function() {
const $this = $(this); const $this = $(this);
new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath')); new CreateLabelDropdown(
$this.closest('.dropdown').find('.dropdown-new-label'),
$this.data('namespacePath'),
$this.data('projectPath'),
);
$this.glDropdown({ $this.glDropdown({
data(term, callback) { data(term, callback) {
axios.get($this.attr('data-list-labels-path')) axios.get($this.attr('data-list-labels-path')).then(({ data }) => {
.then(({ data }) => {
callback(data); callback(data);
}); });
}, },
renderRow (label) { renderRow(label) {
const active = boardsStore.findList('title', label.title); const active = boardsStore.findList('title', label.title);
const $li = $('<li />'); const $li = $('<li />');
const $a = $('<a />', { const $a = $('<a />', {
class: (active ? `is-active js-board-list-${active.id}` : ''), class: active ? `is-active js-board-list-${active.id}` : '',
text: label.title, text: label.title,
href: '#', href: '#',
}); });
...@@ -53,7 +58,7 @@ export default function initNewListDropdown() { ...@@ -53,7 +58,7 @@ export default function initNewListDropdown() {
selectable: true, selectable: true,
multiSelect: true, multiSelect: true,
containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content', containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content',
clicked (options) { clicked(options) {
const { e } = options; const { e } = options;
const label = options.selectedObj; const label = options.selectedObj;
e.preventDefault(); e.preventDefault();
......
...@@ -46,7 +46,7 @@ export default { ...@@ -46,7 +46,7 @@ export default {
selectable: true, selectable: true,
data: (term, callback) => { data: (term, callback) => {
this.loading = true; this.loading = true;
return Api.groupProjects(this.groupId, term, {with_issues_enabled: true}, projects => { return Api.groupProjects(this.groupId, term, { with_issues_enabled: true }, projects => {
this.loading = false; this.loading = false;
callback(projects); callback(projects);
}); });
...@@ -54,7 +54,9 @@ export default { ...@@ -54,7 +54,9 @@ export default {
renderRow(project) { renderRow(project) {
return ` return `
<li> <li>
<a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}"> <a href='#' class='dropdown-menu-link' data-project-id="${
project.id
}" data-project-name="${project.name}">
${_.escape(project.name)} ${_.escape(project.name)}
</a> </a>
</li> </li>
......
<script> <script>
import Vue from 'vue'; import Vue from 'vue';
import Flash from '../../../flash'; import Flash from '../../../flash';
import { __ } from '../../../locale'; import { __ } from '../../../locale';
import boardsStore from '../../stores/boards_store'; import boardsStore from '../../stores/boards_store';
export default Vue.extend({ export default Vue.extend({
props: { props: {
issue: { issue: {
type: Object, type: Object,
...@@ -56,9 +56,7 @@ ...@@ -56,9 +56,7 @@
buildPatchRequest(issue, lists) { buildPatchRequest(issue, lists) {
const listLabelIds = lists.map(list => list.label.id); const listLabelIds = lists.map(list => list.label.id);
const labelIds = issue.labels const labelIds = issue.labels.map(label => label.id).filter(id => !listLabelIds.includes(id));
.map(label => label.id)
.filter(id => !listLabelIds.includes(id));
return { return {
label_ids: labelIds, label_ids: labelIds,
...@@ -73,7 +71,7 @@ ...@@ -73,7 +71,7 @@
return req; return req;
}, },
}, },
}); });
</script> </script>
<template> <template>
<div <div
......
...@@ -32,7 +32,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager { ...@@ -32,7 +32,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token'); const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token');
// Remove all the tokens as they will be replaced by the search manager // Remove all the tokens as they will be replaced by the search manager
[].forEach.call(tokens, (el) => { [].forEach.call(tokens, el => {
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
}); });
...@@ -50,7 +50,10 @@ export default class FilteredSearchBoards extends FilteredSearchManager { ...@@ -50,7 +50,10 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
canEdit(tokenName, tokenValue) { canEdit(tokenName, tokenValue) {
if (this.cantEdit.includes(tokenName)) return false; if (this.cantEdit.includes(tokenName)) return false;
return this.cantEditWithValue.findIndex(token => token.name === tokenName && return (
token.value === tokenValue) === -1; this.cantEditWithValue.findIndex(
token => token.name === tokenName && token.value === tokenValue,
) === -1
);
} }
} }
...@@ -32,9 +32,9 @@ export default () => { ...@@ -32,9 +32,9 @@ export default () => {
const $boardApp = document.getElementById('board-app'); const $boardApp = document.getElementById('board-app');
// check for browser back and trigger a hard reload to circumvent browser caching. // check for browser back and trigger a hard reload to circumvent browser caching.
window.addEventListener('pageshow', (event) => { window.addEventListener('pageshow', event => {
const isNavTypeBackForward = window.performance && const isNavTypeBackForward =
window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD; window.performance && window.performance.navigation.type === NavigationType.TYPE_BACK_FORWARD;
if (event.persisted || isNavTypeBackForward) { if (event.persisted || isNavTypeBackForward) {
window.location.reload(); window.location.reload();
......
...@@ -4,7 +4,8 @@ import $ from 'jquery'; ...@@ -4,7 +4,8 @@ import $ from 'jquery';
import sortableConfig from '../../sortable/sortable_config'; import sortableConfig from '../../sortable/sortable_config';
export function sortableStart() { export function sortableStart() {
$('.has-tooltip').tooltip('hide') $('.has-tooltip')
.tooltip('hide')
.tooltip('disable'); .tooltip('disable');
document.body.classList.add('is-dragging'); document.body.classList.add('is-dragging');
} }
...@@ -15,7 +16,8 @@ export function sortableEnd() { ...@@ -15,7 +16,8 @@ export function sortableEnd() {
} }
export function getBoardSortableDefaultOptions(obj) { export function getBoardSortableDefaultOptions(obj) {
const touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; const touchEnabled =
'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);
const defaultSortOptions = Object.assign({}, sortableConfig, { const defaultSortOptions = Object.assign({}, sortableConfig, {
filter: '.board-delete, .btn', filter: '.board-delete, .btn',
...@@ -26,6 +28,8 @@ export function getBoardSortableDefaultOptions(obj) { ...@@ -26,6 +28,8 @@ export function getBoardSortableDefaultOptions(obj) {
onEnd: sortableEnd, onEnd: sortableEnd,
}); });
Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; }); Object.keys(obj).forEach(key => {
defaultSortOptions[key] = obj[key];
});
return defaultSortOptions; return defaultSortOptions;
} }
...@@ -9,7 +9,7 @@ import IssueProject from './project'; ...@@ -9,7 +9,7 @@ import IssueProject from './project';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
class ListIssue { class ListIssue {
constructor (obj, defaultAvatar) { constructor(obj, defaultAvatar) {
this.id = obj.id; this.id = obj.id;
this.iid = obj.iid; this.iid = obj.iid;
this.title = obj.title; this.title = obj.title;
...@@ -39,54 +39,54 @@ class ListIssue { ...@@ -39,54 +39,54 @@ class ListIssue {
this.milestone = new ListMilestone(obj.milestone); this.milestone = new ListMilestone(obj.milestone);
} }
obj.labels.forEach((label) => { obj.labels.forEach(label => {
this.labels.push(new ListLabel(label)); this.labels.push(new ListLabel(label));
}); });
this.assignees = obj.assignees.map(a => new ListAssignee(a, defaultAvatar)); this.assignees = obj.assignees.map(a => new ListAssignee(a, defaultAvatar));
} }
addLabel (label) { addLabel(label) {
if (!this.findLabel(label)) { if (!this.findLabel(label)) {
this.labels.push(new ListLabel(label)); this.labels.push(new ListLabel(label));
} }
} }
findLabel (findLabel) { findLabel(findLabel) {
return this.labels.filter(label => label.title === findLabel.title)[0]; return this.labels.filter(label => label.title === findLabel.title)[0];
} }
removeLabel (removeLabel) { removeLabel(removeLabel) {
if (removeLabel) { if (removeLabel) {
this.labels = this.labels.filter(label => removeLabel.title !== label.title); this.labels = this.labels.filter(label => removeLabel.title !== label.title);
} }
} }
removeLabels (labels) { removeLabels(labels) {
labels.forEach(this.removeLabel.bind(this)); labels.forEach(this.removeLabel.bind(this));
} }
addAssignee (assignee) { addAssignee(assignee) {
if (!this.findAssignee(assignee)) { if (!this.findAssignee(assignee)) {
this.assignees.push(new ListAssignee(assignee)); this.assignees.push(new ListAssignee(assignee));
} }
} }
findAssignee (findAssignee) { findAssignee(findAssignee) {
return this.assignees.filter(assignee => assignee.id === findAssignee.id)[0]; return this.assignees.filter(assignee => assignee.id === findAssignee.id)[0];
} }
removeAssignee (removeAssignee) { removeAssignee(removeAssignee) {
if (removeAssignee) { if (removeAssignee) {
this.assignees = this.assignees.filter(assignee => assignee.id !== removeAssignee.id); this.assignees = this.assignees.filter(assignee => assignee.id !== removeAssignee.id);
} }
} }
removeAllAssignees () { removeAllAssignees() {
this.assignees = []; this.assignees = [];
} }
getLists () { getLists() {
return boardsStore.state.lists.filter(list => list.findIssue(this.id)); return boardsStore.state.lists.filter(list => list.findIssue(this.id));
} }
...@@ -102,14 +102,14 @@ class ListIssue { ...@@ -102,14 +102,14 @@ class ListIssue {
this.isLoading[key] = value; this.isLoading[key] = value;
} }
update () { update() {
const data = { const data = {
issue: { issue: {
milestone_id: this.milestone ? this.milestone.id : null, milestone_id: this.milestone ? this.milestone.id : null,
due_date: this.dueDate, due_date: this.dueDate,
assignee_ids: this.assignees.length > 0 ? this.assignees.map((u) => u.id) : [0], assignee_ids: this.assignees.length > 0 ? this.assignees.map(u => u.id) : [0],
label_ids: this.labels.map((label) => label.id) label_ids: this.labels.map(label => label.id),
} },
}; };
if (!data.issue.label_ids.length) { if (!data.issue.label_ids.length) {
......
...@@ -234,11 +234,11 @@ class List { ...@@ -234,11 +234,11 @@ class List {
}); });
} }
getTypeInfo (type) { getTypeInfo(type) {
return TYPES[type] || {}; return TYPES[type] || {};
} }
onNewIssueResponse (issue, data) { onNewIssueResponse(issue, data) {
issue.id = data.id; issue.id = data.id;
issue.iid = data.iid; issue.iid = data.iid;
issue.project = data.project; issue.project = data.project;
......
...@@ -19,7 +19,9 @@ export default class BoardService { ...@@ -19,7 +19,9 @@ export default class BoardService {
} }
static generateIssuePath(boardId, id) { static generateIssuePath(boardId, id) {
return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${id ? `/${id}` : ''}`; return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
id ? `/${id}` : ''
}`;
} }
all() { all() {
...@@ -54,7 +56,9 @@ export default class BoardService { ...@@ -54,7 +56,9 @@ export default class BoardService {
getIssuesForList(id, filter = {}) { getIssuesForList(id, filter = {}) {
const data = { id }; const data = { id };
Object.keys(filter).forEach((key) => { data[key] = filter[key]; }); Object.keys(filter).forEach(key => {
data[key] = filter[key];
});
return axios.get(mergeUrlParams(data, this.generateIssuesPath(id))); return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
} }
...@@ -75,7 +79,9 @@ export default class BoardService { ...@@ -75,7 +79,9 @@ export default class BoardService {
} }
getBacklog(data) { getBacklog(data) {
return axios.get(mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`)); return axios.get(
mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`),
);
} }
bulkUpdate(issueIds, extraData = {}) { bulkUpdate(issueIds, extraData = {}) {
......
...@@ -20,20 +20,20 @@ const boardsStore = { ...@@ -20,20 +20,20 @@ const boardsStore = {
issue: {}, issue: {},
list: {}, list: {},
}, },
create () { create() {
this.state.lists = []; this.state.lists = [];
this.filter.path = getUrlParamsArray().join('&'); this.filter.path = getUrlParamsArray().join('&');
this.detail = { this.detail = {
issue: {}, issue: {},
}; };
}, },
addList (listObj, defaultAvatar) { addList(listObj, defaultAvatar) {
const list = new List(listObj, defaultAvatar); const list = new List(listObj, defaultAvatar);
this.state.lists.push(list); this.state.lists.push(list);
return list; return list;
}, },
new (listObj) { new(listObj) {
const list = this.addList(listObj); const list = this.addList(listObj);
const backlogList = this.findList('type', 'backlog', 'backlog'); const backlogList = this.findList('type', 'backlog', 'backlog');
...@@ -50,44 +50,44 @@ const boardsStore = { ...@@ -50,44 +50,44 @@ const boardsStore = {
}); });
this.removeBlankState(); this.removeBlankState();
}, },
updateNewListDropdown (listId) { updateNewListDropdown(listId) {
$(`.js-board-list-${listId}`).removeClass('is-active'); $(`.js-board-list-${listId}`).removeClass('is-active');
}, },
shouldAddBlankState () { shouldAddBlankState() {
// Decide whether to add the blank state // Decide whether to add the blank state
return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]); return !this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0];
}, },
addBlankState () { addBlankState() {
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return; if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
this.addList({ this.addList({
id: 'blank', id: 'blank',
list_type: 'blank', list_type: 'blank',
title: 'Welcome to your Issue Board!', title: 'Welcome to your Issue Board!',
position: 0 position: 0,
}); });
this.state.lists = _.sortBy(this.state.lists, 'position'); this.state.lists = _.sortBy(this.state.lists, 'position');
}, },
removeBlankState () { removeBlankState() {
this.removeList('blank'); this.removeList('blank');
Cookies.set('issue_board_welcome_hidden', 'true', { Cookies.set('issue_board_welcome_hidden', 'true', {
expires: 365 * 10, expires: 365 * 10,
path: '' path: '',
}); });
}, },
welcomeIsHidden () { welcomeIsHidden() {
return Cookies.get('issue_board_welcome_hidden') === 'true'; return Cookies.get('issue_board_welcome_hidden') === 'true';
}, },
removeList (id, type = 'blank') { removeList(id, type = 'blank') {
const list = this.findList('id', id, type); const list = this.findList('id', id, type);
if (!list) return; if (!list) return;
this.state.lists = this.state.lists.filter(list => list.id !== id); this.state.lists = this.state.lists.filter(list => list.id !== id);
}, },
moveList (listFrom, orderLists) { moveList(listFrom, orderLists) {
orderLists.forEach((id, i) => { orderLists.forEach((id, i) => {
const list = this.findList('id', parseInt(id, 10)); const list = this.findList('id', parseInt(id, 10));
...@@ -95,21 +95,24 @@ const boardsStore = { ...@@ -95,21 +95,24 @@ const boardsStore = {
}); });
listFrom.update(); listFrom.update();
}, },
moveIssueToList (listFrom, listTo, issue, newIndex) { moveIssueToList(listFrom, listTo, issue, newIndex) {
const issueTo = listTo.findIssue(issue.id); const issueTo = listTo.findIssue(issue.id);
const issueLists = issue.getLists(); const issueLists = issue.getLists();
const listLabels = issueLists.map(listIssue => listIssue.label); const listLabels = issueLists.map(listIssue => listIssue.label);
if (!issueTo) { if (!issueTo) {
// Check if target list assignee is already present in this issue // Check if target list assignee is already present in this issue
if ((listTo.type === 'assignee' && listFrom.type === 'assignee') && if (
issue.findAssignee(listTo.assignee)) { listTo.type === 'assignee' &&
listFrom.type === 'assignee' &&
issue.findAssignee(listTo.assignee)
) {
const targetIssue = listTo.findIssue(issue.id); const targetIssue = listTo.findIssue(issue.id);
targetIssue.removeAssignee(listFrom.assignee); targetIssue.removeAssignee(listFrom.assignee);
} else if (listTo.type === 'milestone') { } else if (listTo.type === 'milestone') {
const currentMilestone = issue.milestone; const currentMilestone = issue.milestone;
const currentLists = this.state.lists const currentLists = this.state.lists
.filter(list => (list.type === 'milestone' && list.id !== listTo.id)) .filter(list => list.type === 'milestone' && list.id !== listTo.id)
.filter(list => list.issues.some(listIssue => issue.id === listIssue.id)); .filter(list => list.issues.some(listIssue => issue.id === listIssue.id));
issue.removeMilestone(currentMilestone); issue.removeMilestone(currentMilestone);
...@@ -126,7 +129,7 @@ const boardsStore = { ...@@ -126,7 +129,7 @@ const boardsStore = {
} }
if (listTo.type === 'closed' && listFrom.type !== 'backlog') { if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
issueLists.forEach((list) => { issueLists.forEach(list => {
list.removeIssue(issue); list.removeIssue(issue);
}); });
issue.removeLabels(listLabels); issue.removeLabels(listLabels);
...@@ -144,26 +147,28 @@ const boardsStore = { ...@@ -144,26 +147,28 @@ const boardsStore = {
return ( return (
(listTo.type !== 'label' && listFrom.type === 'assignee') || (listTo.type !== 'label' && listFrom.type === 'assignee') ||
(listTo.type !== 'assignee' && listFrom.type === 'label') || (listTo.type !== 'assignee' && listFrom.type === 'label') ||
(listFrom.type === 'backlog') listFrom.type === 'backlog'
); );
}, },
moveIssueInList (list, issue, oldIndex, newIndex, idArray) { moveIssueInList(list, issue, oldIndex, newIndex, idArray) {
const beforeId = parseInt(idArray[newIndex - 1], 10) || null; const beforeId = parseInt(idArray[newIndex - 1], 10) || null;
const afterId = parseInt(idArray[newIndex + 1], 10) || null; const afterId = parseInt(idArray[newIndex + 1], 10) || null;
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId); list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
}, },
findList (key, val, type = 'label') { findList(key, val, type = 'label') {
const filteredList = this.state.lists.filter((list) => { const filteredList = this.state.lists.filter(list => {
const byType = type ? (list.type === type) || (list.type === 'assignee') || (list.type === 'milestone') : true; const byType = type
? list.type === type || list.type === 'assignee' || list.type === 'milestone'
: true;
return list[key] === val && byType; return list[key] === val && byType;
}); });
return filteredList[0]; return filteredList[0];
}, },
updateFiltersUrl () { updateFiltersUrl() {
window.history.pushState(null, null, `?${this.filter.path}`); window.history.pushState(null, null, `?${this.filter.path}`);
} },
}; };
// hacks added in order to allow milestone_select to function properly // hacks added in order to allow milestone_select to function properly
......
...@@ -40,7 +40,7 @@ class ModalStore { ...@@ -40,7 +40,7 @@ class ModalStore {
toggleAll() { toggleAll() {
const select = this.selectedCount() !== this.store.issues.length; const select = this.selectedCount() !== this.store.issues.length;
this.store.issues.forEach((issue) => { this.store.issues.forEach(issue => {
const issueUpdate = issue; const issueUpdate = issue;
if (issueUpdate.selected !== select) { if (issueUpdate.selected !== select) {
...@@ -69,13 +69,14 @@ class ModalStore { ...@@ -69,13 +69,14 @@ class ModalStore {
removeSelectedIssue(issue, forcePurge = false) { removeSelectedIssue(issue, forcePurge = false) {
if (this.store.activeTab === 'all' || forcePurge) { if (this.store.activeTab === 'all' || forcePurge) {
this.store.selectedIssues = this.store.selectedIssues this.store.selectedIssues = this.store.selectedIssues.filter(
.filter(fIssue => fIssue.id !== issue.id); fIssue => fIssue.id !== issue.id,
);
} }
} }
purgeUnselectedIssues() { purgeUnselectedIssues() {
this.store.selectedIssues.forEach((issue) => { this.store.selectedIssues.forEach(issue => {
if (!issue.selected) { if (!issue.selected) {
this.removeSelectedIssue(issue, true); this.removeSelectedIssue(issue, true);
} }
...@@ -87,8 +88,7 @@ class ModalStore { ...@@ -87,8 +88,7 @@ class ModalStore {
} }
findSelectedIssue(issue) { findSelectedIssue(issue) {
return this.store.selectedIssues return this.store.selectedIssues.filter(filteredIssue => filteredIssue.id === issue.id)[0];
.filter(filteredIssue => filteredIssue.id === issue.id)[0];
} }
} }
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import helmInstallIllustration from '@gitlab-org/gitlab-svgs/dist/illustrations/kubernetes-installation.svg'; import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg';
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png'; import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
import gitlabLogo from 'images/cluster_app_logos/gitlab.png'; import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
import helmLogo from 'images/cluster_app_logos/helm.png'; import helmLogo from 'images/cluster_app_logos/helm.png';
......
import Vue from 'vue'; import Vue from 'vue';
import { import { GlProgressBar, GlLoadingIcon, GlTooltipDirective } from '@gitlab-org/gitlab-ui';
GlPagination,
GlProgressBar,
GlModal,
GlLoadingIcon,
GlModalDirective,
GlTooltipDirective,
} from '@gitlab-org/gitlab-ui';
Vue.component('gl-pagination', GlPagination);
Vue.component('gl-progress-bar', GlProgressBar); Vue.component('gl-progress-bar', GlProgressBar);
Vue.component('gl-ui-modal', GlModal);
Vue.component('gl-loading-icon', GlLoadingIcon); Vue.component('gl-loading-icon', GlLoadingIcon);
Vue.directive('gl-modal', GlModalDirective);
Vue.directive('gl-tooltip', GlTooltipDirective); Vue.directive('gl-tooltip', GlTooltipDirective);
...@@ -18,8 +18,8 @@ export default { ...@@ -18,8 +18,8 @@ export default {
}, },
data() { data() {
const treeListStored = localStorage.getItem(treeListStorageKey); const treeListStored = localStorage.getItem(treeListStorageKey);
const renderTreeList = treeListStored !== null ? const renderTreeList =
convertPermissionToBoolean(treeListStored) : true; treeListStored !== null ? convertPermissionToBoolean(treeListStored) : true;
return { return {
search: '', search: '',
......
...@@ -15,7 +15,7 @@ import CommitComponent from '../../vue_shared/components/commit.vue'; ...@@ -15,7 +15,7 @@ import CommitComponent from '../../vue_shared/components/commit.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
/** /**
* Envrionment Item Component * Environment Item Component
* *
* Renders a table row for each environment. * Renders a table row for each environment.
*/ */
...@@ -60,7 +60,7 @@ export default { ...@@ -60,7 +60,7 @@ export default {
computed: { computed: {
/** /**
* Verifies if `last_deployment` key exists in the current Envrionment. * Verifies if `last_deployment` key exists in the current Environment.
* This key is required to render most of the html - this method works has * This key is required to render most of the html - this method works has
* an helper. * an helper.
* *
......
<script> <script>
import Flash from '../../flash'; import Flash from '../../flash';
import { s__ } from '../../locale'; import { s__ } from '../../locale';
import emptyState from './empty_state.vue'; import emptyState from './empty_state.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import environmentsMixin from '../mixins/environments_mixin'; import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import StopEnvironmentModal from './stop_environment_modal.vue'; import StopEnvironmentModal from './stop_environment_modal.vue';
export default { export default {
components: { components: {
emptyState, emptyState,
StopEnvironmentModal, StopEnvironmentModal,
}, },
mixins: [ mixins: [CIPaginationMixin, environmentsMixin],
CIPaginationMixin,
environmentsMixin,
],
props: { props: {
endpoint: { endpoint: {
...@@ -69,7 +66,8 @@ ...@@ -69,7 +66,8 @@
fetchChildEnvironments(folder, showLoader = false) { fetchChildEnvironments(folder, showLoader = false) {
this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader); this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
this.service.getFolderContent(folder.folder_path) this.service
.getFolderContent(folder.folder_path)
.then(response => this.store.setfolderContent(folder, response.data.environments)) .then(response => this.store.setfolderContent(folder, response.data.environments))
.then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false)) .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
.catch(() => { .catch(() => {
...@@ -88,7 +86,7 @@ ...@@ -88,7 +86,7 @@
} }
}, },
}, },
}; };
</script> </script>
<template> <template>
<div :class="cssContainerClass"> <div :class="cssContainerClass">
......
...@@ -34,9 +34,9 @@ export default class EnvironmentsStore { ...@@ -34,9 +34,9 @@ export default class EnvironmentsStore {
* @returns {Array} * @returns {Array}
*/ */
storeEnvironments(environments = []) { storeEnvironments(environments = []) {
const filteredEnvironments = environments.map((env) => { const filteredEnvironments = environments.map(env => {
const oldEnvironmentState = this.state.environments const oldEnvironmentState =
.find((element) => { this.state.environments.find(element => {
if (env.latest) { if (env.latest) {
return element.id === env.latest.id; return element.id === env.latest.id;
} }
...@@ -119,7 +119,7 @@ export default class EnvironmentsStore { ...@@ -119,7 +119,7 @@ export default class EnvironmentsStore {
* @return {Object} * @return {Object}
*/ */
setfolderContent(folder, environments) { setfolderContent(folder, environments) {
const updatedEnvironments = environments.map((env) => { const updatedEnvironments = environments.map(env => {
let updated = env; let updated = env;
if (env.latest) { if (env.latest) {
...@@ -148,7 +148,7 @@ export default class EnvironmentsStore { ...@@ -148,7 +148,7 @@ export default class EnvironmentsStore {
updateEnvironmentProp(environment, prop, newValue) { updateEnvironmentProp(environment, prop, newValue) {
const { environments } = this.state; const { environments } = this.state;
const updatedEnvironments = environments.map((env) => { const updatedEnvironments = environments.map(env => {
const updateEnv = Object.assign({}, env); const updateEnv = Object.assign({}, env);
if (env.id === environment.id) { if (env.id === environment.id) {
updateEnv[prop] = newValue; updateEnv[prop] = newValue;
......
...@@ -39,8 +39,9 @@ export default class DropdownUser extends FilteredSearchDropdown { ...@@ -39,8 +39,9 @@ export default class DropdownUser extends FilteredSearchDropdown {
} }
itemClicked(e) { itemClicked(e) {
super.itemClicked(e, super.itemClicked(e, selected =>
selected => selected.querySelector('.dropdown-light-content').innerText.trim()); selected.querySelector('.dropdown-light-content').innerText.trim(),
);
} }
renderContent(forceShowList = false) { renderContent(forceShowList = false) {
...@@ -68,7 +69,7 @@ export default class DropdownUser extends FilteredSearchDropdown { ...@@ -68,7 +69,7 @@ export default class DropdownUser extends FilteredSearchDropdown {
// Removes the first character if it is a quotation so that we can search // Removes the first character if it is a quotation so that we can search
// with multiple words // with multiple words
if (value[0] === '"' || value[0] === '\'') { if (value[0] === '"' || value[0] === "'") {
value = value.slice(1); value = value.slice(1);
} }
......
...@@ -85,7 +85,7 @@ export default class FilteredSearchDropdown { ...@@ -85,7 +85,7 @@ export default class FilteredSearchDropdown {
} }
dispatchInputEvent() { dispatchInputEvent() {
// Propogate input change to FilteredSearchDropdownManager // Propagate input change to FilteredSearchDropdownManager
// so that it can determine which dropdowns to open // so that it can determine which dropdowns to open
this.input.dispatchEvent( this.input.dispatchEvent(
new CustomEvent('input', { new CustomEvent('input', {
......
...@@ -108,7 +108,7 @@ export default class FilteredSearchDropdownManager { ...@@ -108,7 +108,7 @@ export default class FilteredSearchDropdownManager {
}, },
}; };
supportedTokens.forEach((type) => { supportedTokens.forEach(type => {
if (availableMappings[type]) { if (availableMappings[type]) {
allowedMappings[type] = availableMappings[type]; allowedMappings[type] = availableMappings[type];
} }
...@@ -142,10 +142,7 @@ export default class FilteredSearchDropdownManager { ...@@ -142,10 +142,7 @@ export default class FilteredSearchDropdownManager {
} }
static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) { static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) {
const { const { uppercaseTokenName = false, capitalizeTokenValue = false } = options;
uppercaseTokenName = false,
capitalizeTokenValue = false,
} = options;
const input = FilteredSearchContainer.container.querySelector('.filtered-search'); const input = FilteredSearchContainer.container.querySelector('.filtered-search');
FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue, { FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue, {
uppercaseTokenName, uppercaseTokenName,
...@@ -164,13 +161,16 @@ export default class FilteredSearchDropdownManager { ...@@ -164,13 +161,16 @@ export default class FilteredSearchDropdownManager {
updateDropdownOffset(key) { updateDropdownOffset(key) {
// Always align dropdown with the input field // Always align dropdown with the input field
let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left; let offset =
this.filteredSearchInput.getBoundingClientRect().left -
this.container.querySelector('.scroll-container').getBoundingClientRect().left;
const maxInputWidth = 240; const maxInputWidth = 240;
const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth; const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
// Make sure offset never exceeds the input container // Make sure offset never exceeds the input container
const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth; const offsetMaxWidth =
this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) { if (offsetMaxWidth < offset) {
offset = offsetMaxWidth; offset = offsetMaxWidth;
} }
...@@ -196,8 +196,7 @@ export default class FilteredSearchDropdownManager { ...@@ -196,8 +196,7 @@ export default class FilteredSearchDropdownManager {
const glArguments = Object.assign({}, defaultArguments, extraArguments); const glArguments = Object.assign({}, defaultArguments, extraArguments);
// Passing glArguments to `new glClass(<arguments>)` // Passing glArguments to `new glClass(<arguments>)`
mappingKey.reference = mappingKey.reference = new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
} }
if (firstLoad) { if (firstLoad) {
...@@ -224,8 +223,8 @@ export default class FilteredSearchDropdownManager { ...@@ -224,8 +223,8 @@ export default class FilteredSearchDropdownManager {
} }
const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase()); const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key const shouldOpenFilterDropdown =
&& this.mapping[match.key]; match && this.currentDropdown !== match.key && this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint'; const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) { if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
...@@ -236,8 +235,10 @@ export default class FilteredSearchDropdownManager { ...@@ -236,8 +235,10 @@ export default class FilteredSearchDropdownManager {
setDropdown() { setDropdown() {
const query = DropdownUtils.getSearchQuery(true); const query = DropdownUtils.getSearchQuery(true);
const { lastToken, searchToken } = const { lastToken, searchToken } = this.tokenizer.processTokens(
this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys()); query,
this.filteredSearchTokenKeys.getKeys(),
);
if (this.currentDropdown) { if (this.currentDropdown) {
this.updateCurrentDropdownOffset(); this.updateCurrentDropdownOffset();
......
...@@ -68,12 +68,12 @@ export const conditions = [ ...@@ -68,12 +68,12 @@ export const conditions = [
value: 'any', value: 'any',
}, },
{ {
url: 'milestone_title=No+Milestone', url: 'milestone_title=None',
tokenKey: 'milestone', tokenKey: 'milestone',
value: 'none', value: 'none',
}, },
{ {
url: 'milestone_title=Any+Milestone', url: 'milestone_title=Any',
tokenKey: 'milestone', tokenKey: 'milestone',
value: 'any', value: 'any',
}, },
...@@ -92,6 +92,16 @@ export const conditions = [ ...@@ -92,6 +92,16 @@ export const conditions = [
tokenKey: 'label', tokenKey: 'label',
value: 'none', value: 'none',
}, },
{
url: 'my_reaction_emoji=None',
tokenKey: 'my-reaction',
value: 'none',
},
{
url: 'my_reaction_emoji=Any',
tokenKey: 'my-reaction',
value: 'any',
},
]; ];
const IssuableFilteredSearchTokenKeys = new FilteredSearchTokenKeys( const IssuableFilteredSearchTokenKeys = new FilteredSearchTokenKeys(
......
...@@ -40,7 +40,9 @@ const createFlashEl = (message, type, isFixedLayout = false) => ` ...@@ -40,7 +40,9 @@ const createFlashEl = (message, type, isFixedLayout = false) => `
class="flash-${type}" class="flash-${type}"
> >
<div <div
class="flash-text ${isFixedLayout ? 'container-fluid container-limited limit-container-width' : ''}" class="flash-text ${
isFixedLayout ? 'container-fluid container-limited limit-container-width' : ''
}"
> >
${_.escape(message)} ${_.escape(message)}
</div> </div>
...@@ -78,7 +80,9 @@ const createFlash = function createFlash( ...@@ -78,7 +80,9 @@ const createFlash = function createFlash(
if (!flashContainer) return null; if (!flashContainer) return null;
const isFixedLayout = navigation ? navigation.parentNode.classList.contains('container-limited') : true; const isFixedLayout = navigation
? navigation.parentNode.classList.contains('container-limited')
: true;
flashContainer.innerHTML = createFlashEl(message, type, isFixedLayout); flashContainer.innerHTML = createFlashEl(message, type, isFixedLayout);
......
...@@ -94,7 +94,7 @@ class GfmAutoComplete { ...@@ -94,7 +94,7 @@ class GfmAutoComplete {
...this.getDefaultCallbacks(), ...this.getDefaultCallbacks(),
beforeSave(commands) { beforeSave(commands) {
if (GfmAutoComplete.isLoading(commands)) return commands; if (GfmAutoComplete.isLoading(commands)) return commands;
return $.map(commands, (c) => { return $.map(commands, c => {
let search = c.name; let search = c.name;
if (c.aliases.length > 0) { if (c.aliases.length > 0) {
search = `${search} ${c.aliases.join(' ')}`; search = `${search} ${c.aliases.join(' ')}`;
...@@ -167,7 +167,7 @@ class GfmAutoComplete { ...@@ -167,7 +167,7 @@ class GfmAutoComplete {
callbacks: { callbacks: {
...this.getDefaultCallbacks(), ...this.getDefaultCallbacks(),
beforeSave(members) { beforeSave(members) {
return $.map(members, (m) => { return $.map(members, m => {
let title = ''; let title = '';
if (m.username == null) { if (m.username == null) {
return m; return m;
...@@ -178,7 +178,9 @@ class GfmAutoComplete { ...@@ -178,7 +178,9 @@ class GfmAutoComplete {
} }
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase(); const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`; const imgAvatar = `<img src="${m.avatar_url}" alt="${
m.username
}" class="avatar avatar-inline center s26"/>`;
const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`; const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
return { return {
...@@ -211,7 +213,7 @@ class GfmAutoComplete { ...@@ -211,7 +213,7 @@ class GfmAutoComplete {
callbacks: { callbacks: {
...this.getDefaultCallbacks(), ...this.getDefaultCallbacks(),
beforeSave(issues) { beforeSave(issues) {
return $.map(issues, (i) => { return $.map(issues, i => {
if (i.title == null) { if (i.title == null) {
return i; return i;
} }
...@@ -244,7 +246,7 @@ class GfmAutoComplete { ...@@ -244,7 +246,7 @@ class GfmAutoComplete {
callbacks: { callbacks: {
...this.getDefaultCallbacks(), ...this.getDefaultCallbacks(),
beforeSave(milestones) { beforeSave(milestones) {
return $.map(milestones, (m) => { return $.map(milestones, m => {
if (m.title == null) { if (m.title == null) {
return m; return m;
} }
...@@ -277,7 +279,7 @@ class GfmAutoComplete { ...@@ -277,7 +279,7 @@ class GfmAutoComplete {
callbacks: { callbacks: {
...this.getDefaultCallbacks(), ...this.getDefaultCallbacks(),
beforeSave(merges) { beforeSave(merges) {
return $.map(merges, (m) => { return $.map(merges, m => {
if (m.title == null) { if (m.title == null) {
return m; return m;
} }
...@@ -324,13 +326,20 @@ class GfmAutoComplete { ...@@ -324,13 +326,20 @@ class GfmAutoComplete {
}, },
matcher(flag, subtext) { matcher(flag, subtext) {
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers); const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
const subtextNodes = subtext.split(/\n+/g).pop().split(GfmAutoComplete.regexSubtext); const subtextNodes = subtext
.split(/\n+/g)
.pop()
.split(GfmAutoComplete.regexSubtext);
// Check if ~ is followed by '/label', '/relabel' or '/unlabel' commands. // Check if ~ is followed by '/label', '/relabel' or '/unlabel' commands.
command = subtextNodes.find((node) => { command = subtextNodes.find(node => {
if (node === LABEL_COMMAND.LABEL || if (
node === LABEL_COMMAND.LABEL ||
node === LABEL_COMMAND.RELABEL || node === LABEL_COMMAND.RELABEL ||
node === LABEL_COMMAND.UNLABEL) { return node; } node === LABEL_COMMAND.UNLABEL
) {
return node;
}
return null; return null;
}); });
...@@ -380,7 +389,7 @@ class GfmAutoComplete { ...@@ -380,7 +389,7 @@ class GfmAutoComplete {
callbacks: { callbacks: {
...this.getDefaultCallbacks(), ...this.getDefaultCallbacks(),
beforeSave(snippets) { beforeSave(snippets) {
return $.map(snippets, (m) => { return $.map(snippets, m => {
if (m.title == null) { if (m.title == null) {
return m; return m;
} }
...@@ -458,13 +467,17 @@ class GfmAutoComplete { ...@@ -458,13 +467,17 @@ class GfmAutoComplete {
this.loadData($input, at, validEmojiNames); this.loadData($input, at, validEmojiNames);
GfmAutoComplete.glEmojiTag = glEmojiTag; GfmAutoComplete.glEmojiTag = glEmojiTag;
}) })
.catch(() => { this.isLoadingData[at] = false; }); .catch(() => {
this.isLoadingData[at] = false;
});
} else if (dataSource) { } else if (dataSource) {
AjaxCache.retrieve(dataSource, true) AjaxCache.retrieve(dataSource, true)
.then((data) => { .then(data => {
this.loadData($input, at, data); this.loadData($input, at, data);
}) })
.catch(() => { this.isLoadingData[at] = false; }); .catch(() => {
this.isLoadingData[at] = false;
});
} else { } else {
this.isLoadingData[at] = false; this.isLoadingData[at] = false;
} }
...@@ -497,15 +510,16 @@ class GfmAutoComplete { ...@@ -497,15 +510,16 @@ class GfmAutoComplete {
} }
const loadingState = GfmAutoComplete.defaultLoadingData[0]; const loadingState = GfmAutoComplete.defaultLoadingData[0];
return dataToInspect && return dataToInspect && (dataToInspect === loadingState || dataToInspect.name === loadingState);
(dataToInspect === loadingState || dataToInspect.name === loadingState);
} }
static defaultMatcher(flag, subtext, controllers) { static defaultMatcher(flag, subtext, controllers) {
// The below is taken from At.js source // The below is taken from At.js source
// Tweaked to commands to start without a space only if char before is a non-word character // Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js // https://github.com/ichord/At.js
const atSymbolsWithBar = Object.keys(controllers).join('|').replace(/[$]/, '\\$&'); const atSymbolsWithBar = Object.keys(controllers)
.join('|')
.replace(/[$]/, '\\$&');
const atSymbolsWithoutBar = Object.keys(controllers).join(''); const atSymbolsWithoutBar = Object.keys(controllers).join('');
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop(); const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
...@@ -513,7 +527,10 @@ class GfmAutoComplete { ...@@ -513,7 +527,10 @@ class GfmAutoComplete {
const accentAChar = decodeURI('%C3%80'); const accentAChar = decodeURI('%C3%80');
const accentYChar = decodeURI('%C3%BF'); const accentYChar = decodeURI('%C3%BF');
const regexp = new RegExp(`^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, 'gi'); const regexp = new RegExp(
`^(?:\\B|[^a-zA-Z0-9_\`${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`,
'gi',
);
return regexp.exec(targetSubtext); return regexp.exec(targetSubtext);
} }
...@@ -552,8 +569,9 @@ GfmAutoComplete.Members = { ...@@ -552,8 +569,9 @@ GfmAutoComplete.Members = {
template: '<li>${avatarTag} ${username} <small>${title}</small></li>', template: '<li>${avatarTag} ${username} <small>${title}</small></li>',
}; };
GfmAutoComplete.Labels = { GfmAutoComplete.Labels = {
template:
// eslint-disable-next-line no-template-curly-in-string // eslint-disable-next-line no-template-curly-in-string
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>', '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>',
}; };
// Issues, MergeRequests and Snippets // Issues, MergeRequests and Snippets
GfmAutoComplete.Issues = { GfmAutoComplete.Issues = {
...@@ -567,7 +585,8 @@ GfmAutoComplete.Milestones = { ...@@ -567,7 +585,8 @@ GfmAutoComplete.Milestones = {
template: '<li>${title}</li>', template: '<li>${title}</li>',
}; };
GfmAutoComplete.Loading = { GfmAutoComplete.Loading = {
template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>', template:
'<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>',
}; };
export default GfmAutoComplete; export default GfmAutoComplete;
...@@ -28,7 +28,7 @@ export default class GlFieldErrors { ...@@ -28,7 +28,7 @@ export default class GlFieldErrors {
this.form.on('submit', GlFieldErrors.catchInvalidFormSubmit); this.form.on('submit', GlFieldErrors.catchInvalidFormSubmit);
} }
/* Neccessary to prevent intercept and override invalid form submit /* Necessary to prevent intercept and override invalid form submit
* because Safari & iOS quietly allow form submission when form is invalid * because Safari & iOS quietly allow form submission when form is invalid
* and prevents disabling of invalid submit button by application.js */ * and prevents disabling of invalid submit button by application.js */
......
import $ from 'jquery'; import $ from 'jquery';
import { slugifyWithHyphens } from './lib/utils/text_utility';
export default class Group { export default class Group {
constructor() { constructor() {
...@@ -7,17 +8,18 @@ export default class Group { ...@@ -7,17 +8,18 @@ export default class Group {
this.updateHandler = this.update.bind(this); this.updateHandler = this.update.bind(this);
this.resetHandler = this.reset.bind(this); this.resetHandler = this.reset.bind(this);
if (this.groupName.val() === '') { if (this.groupName.val() === '') {
this.groupPath.on('keyup', this.updateHandler); this.groupName.on('keyup', this.updateHandler);
this.groupName.on('keydown', this.resetHandler); this.groupPath.on('keydown', this.resetHandler);
} }
} }
update() { update() {
this.groupName.val(this.groupPath.val()); const slug = slugifyWithHyphens(this.groupName.val());
this.groupPath.val(slug);
} }
reset() { reset() {
this.groupPath.off('keyup', this.updateHandler); this.groupName.off('keyup', this.updateHandler);
this.groupName.off('keydown', this.resetHandler); this.groupPath.off('keydown', this.resetHandler);
} }
} }
...@@ -43,7 +43,7 @@ export default { ...@@ -43,7 +43,7 @@ export default {
'currentProjectId', 'currentProjectId',
'errorMessage', 'errorMessage',
]), ]),
...mapGetters(['activeFile', 'hasChanges', 'someUncommitedChanges', 'isCommitModeActive']), ...mapGetters(['activeFile', 'hasChanges', 'someUncommittedChanges', 'isCommitModeActive']),
}, },
mounted() { mounted() {
window.onbeforeunload = e => this.onBeforeUnload(e); window.onbeforeunload = e => this.onBeforeUnload(e);
...@@ -63,7 +63,7 @@ export default { ...@@ -63,7 +63,7 @@ export default {
onBeforeUnload(e = {}) { onBeforeUnload(e = {}) {
const returnValue = __('Are you sure you want to lose unsaved changes?'); const returnValue = __('Are you sure you want to lose unsaved changes?');
if (!this.someUncommitedChanges) return undefined; if (!this.someUncommittedChanges) return undefined;
Object.assign(e, { Object.assign(e, {
returnValue, returnValue,
......
...@@ -25,11 +25,11 @@ export default { ...@@ -25,11 +25,11 @@ export default {
}, },
computed: { computed: {
...mapState(['loading', 'currentActivityView', 'changedFiles', 'stagedFiles', 'lastCommitMsg']), ...mapState(['loading', 'currentActivityView', 'changedFiles', 'stagedFiles', 'lastCommitMsg']),
...mapGetters(['currentProject', 'someUncommitedChanges']), ...mapGetters(['currentProject', 'someUncommittedChanges']),
showSuccessMessage() { showSuccessMessage() {
return ( return (
this.currentActivityView === activityBarViews.edit && this.currentActivityView === activityBarViews.edit &&
(this.lastCommitMsg && !this.someUncommitedChanges) (this.lastCommitMsg && !this.someUncommittedChanges)
); );
}, },
}, },
......
...@@ -27,10 +27,10 @@ export default { ...@@ -27,10 +27,10 @@ export default {
'unusedSeal', 'unusedSeal',
]), ]),
...mapState('commit', ['commitMessage', 'submitCommitLoading']), ...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges', 'activeFile']), ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommittedChanges', 'activeFile']),
...mapGetters('commit', ['discardDraftButtonDisabled']), ...mapGetters('commit', ['discardDraftButtonDisabled']),
showStageUnstageArea() { showStageUnstageArea() {
return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal); return !!(this.someUncommittedChanges || this.lastCommitMsg || !this.unusedSeal);
}, },
activeFileKey() { activeFileKey() {
return this.activeFile ? this.activeFile.key : null; return this.activeFile ? this.activeFile.key : null;
......
...@@ -63,7 +63,7 @@ export const isEditModeActive = state => state.currentActivityView === activityB ...@@ -63,7 +63,7 @@ export const isEditModeActive = state => state.currentActivityView === activityB
export const isCommitModeActive = state => state.currentActivityView === activityBarViews.commit; export const isCommitModeActive = state => state.currentActivityView === activityBarViews.commit;
export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review; export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review;
export const someUncommitedChanges = state => export const someUncommittedChanges = state =>
!!(state.changedFiles.length || state.stagedFiles.length); !!(state.changedFiles.length || state.stagedFiles.length);
export const getChangesInFolder = state => path => { export const getChangesInFolder = state => path => {
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapGetters, mapState, mapActions } from 'vuex';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils'; import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
import bp from '~/breakpoints'; import bp from '~/breakpoints';
import CiHeader from '~/vue_shared/components/header_ci_component.vue'; import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import Callout from '~/vue_shared/components/callout.vue'; import Callout from '~/vue_shared/components/callout.vue';
import createStore from '../store'; import createStore from '../store';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue'; import EnvironmentsBlock from './environments_block.vue';
import ErasedBlock from './erased_block.vue'; import ErasedBlock from './erased_block.vue';
import Log from './job_log.vue'; import Log from './job_log.vue';
import LogTopBar from './job_log_controllers.vue'; import LogTopBar from './job_log_controllers.vue';
import StuckBlock from './stuck_block.vue'; import StuckBlock from './stuck_block.vue';
import Sidebar from './sidebar.vue'; import Sidebar from './sidebar.vue';
export default { export default {
name: 'JobPageApp', name: 'JobPageApp',
store: createStore(), store: createStore(),
components: { components: {
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
shouldRenderContent() { shouldRenderContent() {
return !this.isLoading && !this.hasError; return !this.isLoading && !this.hasError;
} },
}, },
watch: { watch: {
// Once the job log is loaded, // Once the job log is loaded,
...@@ -158,7 +158,7 @@ ...@@ -158,7 +158,7 @@
this.throttled(); this.throttled();
}, },
}, },
}; };
</script> </script>
<template> <template>
<div> <div>
......
<script> <script>
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
export default { export default {
name: 'JobLog', name: 'JobLog',
props: { props: {
trace: { trace: {
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
} }
}, },
}, },
}; };
</script> </script>
<template> <template>
<pre class="js-build-trace build-trace qa-build-trace"> <pre class="js-build-trace build-trace qa-build-trace">
......
...@@ -23,4 +23,3 @@ export default () => { ...@@ -23,4 +23,3 @@ export default () => {
}, },
}); });
}; };
...@@ -35,16 +35,19 @@ export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status); ...@@ -35,16 +35,19 @@ export const hasEnvironment = state => !_.isEmpty(state.job.deployment_status);
* Used to check if it should render the job log or the empty state * Used to check if it should render the job log or the empty state
* @returns {Boolean} * @returns {Boolean}
*/ */
export const hasTrace = state => state.job.has_trace || (!_.isEmpty(state.job.status) && state.job.status.group === 'running'); export const hasTrace = state =>
state.job.has_trace || (!_.isEmpty(state.job.status) && state.job.status.group === 'running');
export const emptyStateIllustration = state => export const emptyStateIllustration = state =>
(state.job && state.job.status && state.job.status.illustration) || {}; (state.job && state.job.status && state.job.status.illustration) || {};
export const emptyStateAction = state => (state.job && state.job.status && state.job.status.action) || {}; export const emptyStateAction = state =>
(state.job && state.job.status && state.job.status.action) || {};
export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete; export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete;
export const hasRunnersForProject = state => state.job.runners.available && !state.job.runners.online; export const hasRunnersForProject = state =>
state.job.runners.available && !state.job.runners.online;
// prevent babel-plugin-rewire from generating an invalid default during karma tests // prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {}; export default () => {};
This diff is collapsed.
/* global ace */ /* global ace */
export default function getModeByFileExtension(path) { export default function getModeByFileExtension(path) {
const modelist = ace.require("ace/ext/modelist"); const modelist = ace.require('ace/ext/modelist');
return modelist.getModeForPath(path).mode; return modelist.getModeForPath(path).mode;
}; }
...@@ -7,7 +7,7 @@ import { BYTES_IN_KIB } from './constants'; ...@@ -7,7 +7,7 @@ import { BYTES_IN_KIB } from './constants';
* * * Show 3 digits to the right * * * Show 3 digits to the right
* * For 2 digits to the left of the decimal point and X digits to the right of it * * For 2 digits to the left of the decimal point and X digits to the right of it
* * * Show 2 digits to the right * * * Show 2 digits to the right
*/ */
export function formatRelevantDigits(number) { export function formatRelevantDigits(number) {
let digitsLeft = ''; let digitsLeft = '';
let relevantDigits = 0; let relevantDigits = 0;
......
...@@ -7,8 +7,12 @@ export default class Members { ...@@ -7,8 +7,12 @@ export default class Members {
} }
addListeners() { addListeners() {
$('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this)); $('.js-member-update-control')
$('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this)); .off('change')
.on('change', this.formSubmit.bind(this));
$('.js-edit-member-form')
.off('ajax:success')
.on('ajax:success', this.formSuccess.bind(this));
gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
} }
...@@ -28,7 +32,7 @@ export default class Members { ...@@ -28,7 +32,7 @@ export default class Members {
toggleLabel(selected, $el) { toggleLabel(selected, $el) {
return $el.text(); return $el.text();
}, },
clicked: (options) => { clicked: options => {
this.formSubmit(null, options.$el); this.formSubmit(null, options.$el);
}, },
}); });
......
...@@ -9,7 +9,10 @@ import '~/gl_dropdown'; ...@@ -9,7 +9,10 @@ import '~/gl_dropdown';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { timeFor } from './lib/utils/datetime_utility'; import { timeFor } from './lib/utils/datetime_utility';
import ModalStore from './boards/stores/modal_store'; import ModalStore from './boards/stores/modal_store';
import boardsStore, { boardStoreIssueSet, boardStoreIssueDelete } from './boards/stores/boards_store'; import boardsStore, {
boardStoreIssueSet,
boardStoreIssueDelete,
} from './boards/stores/boards_store';
export default class MilestoneSelect { export default class MilestoneSelect {
constructor(currentProject, els, options = {}) { constructor(currentProject, els, options = {}) {
......
...@@ -110,7 +110,7 @@ export default { ...@@ -110,7 +110,7 @@ export default {
// Get the remaining list to use in `and x more` text. // Get the remaining list to use in `and x more` text.
const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length); const remainingAwardList = awardList.slice(TOOLTIP_NAME_COUNT, awardList.length);
// Add myself to the begining of the list so title will start with You. // Add myself to the beginning of the list so title will start with You.
if (hasReactionByCurrentUser) { if (hasReactionByCurrentUser) {
namesToShow.unshift('You'); namesToShow.unshift('You');
} }
......
...@@ -54,7 +54,13 @@ export default { ...@@ -54,7 +54,13 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters(['isNotesFetched', 'discussions', 'getNotesDataByProp', 'discussionCount', 'isLoading']), ...mapGetters([
'isNotesFetched',
'discussions',
'getNotesDataByProp',
'discussionCount',
'isLoading',
]),
noteableType() { noteableType() {
return this.noteableData.noteableType; return this.noteableData.noteableType;
}, },
......
import Vue from 'vue'; import Vue from 'vue';
import DiscussionFilter from './components/discussion_filter.vue'; import DiscussionFilter from './components/discussion_filter.vue';
export default (store) => { export default store => {
const discussionFilterEl = document.getElementById('js-vue-discussion-filter'); const discussionFilterEl = document.getElementById('js-vue-discussion-filter');
if (discussionFilterEl) { if (discussionFilterEl) {
const { defaultFilter, notesFilters } = discussionFilterEl.dataset; const { defaultFilter, notesFilters } = discussionFilterEl.dataset;
const defaultValue = defaultFilter ? parseInt(defaultFilter, 10) : null; const defaultValue = defaultFilter ? parseInt(defaultFilter, 10) : null;
const filterValues = notesFilters ? JSON.parse(notesFilters) : {}; const filterValues = notesFilters ? JSON.parse(notesFilters) : {};
const filters = Object.keys(filterValues).map(entry => const filters = Object.keys(filterValues).map(entry => ({
({ title: entry, value: filterValues[entry] })); title: entry,
value: filterValues[entry],
}));
return new Vue({ return new Vue({
el: discussionFilterEl, el: discussionFilterEl,
......
...@@ -70,7 +70,7 @@ export const collapseSystemNotes = notes => { ...@@ -70,7 +70,7 @@ export const collapseSystemNotes = notes => {
} else if (lastDescriptionSystemNote) { } else if (lastDescriptionSystemNote) {
const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note); const timeDifferenceMinutes = getTimeDifferenceMinutes(lastDescriptionSystemNote, note);
// are they less than 10 minutes appart? // are they less than 10 minutes apart?
if (timeDifferenceMinutes > 10) { if (timeDifferenceMinutes > 10) {
// reset counter // reset counter
descriptionChangedTimes = 1; descriptionChangedTimes = 1;
......
<script> <script>
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import GlModal from '~/vue_shared/components/gl_modal.vue'; import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
export default { export default {
components: { components: {
GlModal, GlModal,
}, },
...@@ -26,29 +26,41 @@ ...@@ -26,29 +26,41 @@
}, },
computed: { computed: {
title() { title() {
return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), { milestoneTitle: this.milestoneTitle }); return sprintf(s__('Milestones|Promote %{milestoneTitle} to group milestone?'), {
milestoneTitle: this.milestoneTitle,
});
}, },
text() { text() {
return sprintf(s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. return sprintf(
s__(`Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}.
Existing project milestones with the same title will be merged. Existing project milestones with the same title will be merged.
This action cannot be reversed.`), { milestoneTitle: this.milestoneTitle, groupName: this.groupName }); This action cannot be reversed.`),
{ milestoneTitle: this.milestoneTitle, groupName: this.groupName },
);
}, },
}, },
methods: { methods: {
onSubmit() { onSubmit() {
eventHub.$emit('promoteMilestoneModal.requestStarted', this.url); eventHub.$emit('promoteMilestoneModal.requestStarted', this.url);
return axios.post(this.url, { params: { format: 'json' } }) return axios
.then((response) => { .post(this.url, { params: { format: 'json' } })
eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: true }); .then(response => {
eventHub.$emit('promoteMilestoneModal.requestFinished', {
milestoneUrl: this.url,
successful: true,
});
visitUrl(response.data.url); visitUrl(response.data.url);
}) })
.catch((error) => { .catch(error => {
eventHub.$emit('promoteMilestoneModal.requestFinished', { milestoneUrl: this.url, successful: false }); eventHub.$emit('promoteMilestoneModal.requestFinished', {
milestoneUrl: this.url,
successful: false,
});
createFlash(error); createFlash(error);
}); });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-modal <gl-modal
...@@ -65,4 +77,3 @@ ...@@ -65,4 +77,3 @@
{{ text }} {{ text }}
</gl-modal> </gl-modal>
</template> </template>
import Vue from 'vue';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
document.addEventListener('DOMContentLoaded', () => {
const remainingTimeElements = document.querySelectorAll('.js-remaining-time');
remainingTimeElements.forEach(
el =>
new Vue({
...GlCountdown,
el,
propsData: {
endDateString: el.dateTime,
},
}),
);
});
...@@ -64,7 +64,9 @@ export default class Project { ...@@ -64,7 +64,9 @@ export default class Project {
const projectId = $(this).data('project-id'); const projectId = $(this).data('project-id');
const cookieKey = `hide_auto_devops_implicitly_enabled_banner_${projectId}`; const cookieKey = `hide_auto_devops_implicitly_enabled_banner_${projectId}`;
Cookies.set(cookieKey, 'false'); Cookies.set(cookieKey, 'false');
$(this).parents('.auto-devops-implicitly-enabled-banner').remove(); $(this)
.parents('.auto-devops-implicitly-enabled-banner')
.remove();
return e.preventDefault(); return e.preventDefault();
}); });
Project.projectSelectDropdown(); Project.projectSelectDropdown();
......
<script> <script>
import projectFeatureSetting from './project_feature_setting.vue'; import projectFeatureSetting from './project_feature_setting.vue';
import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue'; import projectFeatureToggle from '../../../../../vue_shared/components/toggle_button.vue';
import projectSettingRow from './project_setting_row.vue'; import projectSettingRow from './project_setting_row.vue';
import { visibilityOptions, visibilityLevelDescriptions } from '../constants'; import { visibilityOptions, visibilityLevelDescriptions } from '../constants';
import { toggleHiddenClassBySelector } from '../external'; import { toggleHiddenClassBySelector } from '../external';
export default { export default {
components: { components: {
projectFeatureSetting, projectFeatureSetting,
projectFeatureToggle, projectFeatureToggle,
...@@ -91,9 +91,7 @@ ...@@ -91,9 +91,7 @@
computed: { computed: {
featureAccessLevelOptions() { featureAccessLevelOptions() {
const options = [ const options = [[10, 'Only Project Members']];
[10, 'Only Project Members'],
];
if (this.visibilityLevel !== visibilityOptions.PRIVATE) { if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
options.push([20, 'Everyone With Access']); options.push([20, 'Everyone With Access']);
} }
...@@ -196,7 +194,7 @@ ...@@ -196,7 +194,7 @@
return this.allowedVisibilityOptions.includes(option); return this.allowedVisibilityOptions.includes(option);
}, },
}, },
}; };
</script> </script>
<template> <template>
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import { GlModal, GlModalDirective } from '@gitlab-org/gitlab-ui';
export default { export default {
components: {
GlModal,
},
directives: {
'gl-modal': GlModalDirective,
},
props: { props: {
deleteWikiUrl: { deleteWikiUrl: {
type: String, type: String,
...@@ -54,7 +61,7 @@ export default { ...@@ -54,7 +61,7 @@ export default {
> >
{{ __('Delete') }} {{ __('Delete') }}
</button> </button>
<gl-ui-modal <gl-modal
:title="title" :title="title"
:ok-title="s__('WikiPageConfirmDelete|Delete page')" :ok-title="s__('WikiPageConfirmDelete|Delete page')"
:modal-id="modalId" :modal-id="modalId"
...@@ -81,6 +88,6 @@ export default { ...@@ -81,6 +88,6 @@ export default {
name="authenticity_token" name="authenticity_token"
/> />
</form> </form>
</gl-ui-modal> </gl-modal>
</div> </div>
</template> </template>
<script> <script>
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import icon from '../../vue_shared/components/icon.vue'; import Icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
...@@ -10,7 +10,7 @@ export default { ...@@ -10,7 +10,7 @@ export default {
tooltip, tooltip,
}, },
components: { components: {
icon, Icon,
GlCountdown, GlCountdown,
}, },
props: { props: {
......
<script> <script>
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue'; import Icon from '../../vue_shared/components/icon.vue';
export default { export default {
directives: { directives: {
tooltip, tooltip,
}, },
components: { components: {
icon, Icon,
}, },
props: { props: {
artifacts: { artifacts: {
......
...@@ -88,25 +88,25 @@ export default { ...@@ -88,25 +88,25 @@ export default {
class="table-section section-10 js-pipeline-status pipeline-status" class="table-section section-10 js-pipeline-status pipeline-status"
role="rowheader" role="rowheader"
> >
Status {{ s__('Pipeline|Status') }}
</div> </div>
<div <div
class="table-section section-15 js-pipeline-info pipeline-info" class="table-section section-15 js-pipeline-info pipeline-info"
role="rowheader" role="rowheader"
> >
Pipeline {{ s__('Pipeline|Pipeline') }}
</div> </div>
<div <div
class="table-section section-20 js-pipeline-commit pipeline-commit" class="table-section section-20 js-pipeline-commit pipeline-commit"
role="rowheader" role="rowheader"
> >
Commit {{ s__('Pipeline|Commit') }}
</div> </div>
<div <div
class="table-section section-20 js-pipeline-stages pipeline-stages" class="table-section section-20 js-pipeline-stages pipeline-stages"
role="rowheader" role="rowheader"
> >
Stages {{ s__('Pipeline|Stages') }}
</div> </div>
</div> </div>
<pipelines-table-row-component <pipelines-table-row-component
......
...@@ -261,7 +261,7 @@ export default { ...@@ -261,7 +261,7 @@ export default {
class="table-mobile-header" class="table-mobile-header"
role="rowheader" role="rowheader"
> >
Status {{ s__('Pipeline|Status') }}
</div> </div>
<div class="table-mobile-content"> <div class="table-mobile-content">
<ci-badge <ci-badge
...@@ -279,8 +279,9 @@ export default { ...@@ -279,8 +279,9 @@ export default {
<div class="table-section section-20"> <div class="table-section section-20">
<div <div
class="table-mobile-header" class="table-mobile-header"
role="rowheader"> role="rowheader"
Commit >
{{ s__('Pipeline|Commit') }}
</div> </div>
<div class="table-mobile-content"> <div class="table-mobile-content">
<commit-component <commit-component
...@@ -298,8 +299,9 @@ export default { ...@@ -298,8 +299,9 @@ export default {
<div class="table-section section-wrap section-20 stage-cell"> <div class="table-section section-wrap section-20 stage-cell">
<div <div
class="table-mobile-header" class="table-mobile-header"
role="rowheader"> role="rowheader"
Stages >
{{ s__('Pipeline|Stages') }}
</div> </div>
<div class="table-mobile-content"> <div class="table-mobile-content">
<template v-if="pipeline.details.stages.length > 0"> <template v-if="pipeline.details.stages.length > 0">
......
...@@ -60,7 +60,7 @@ export default { ...@@ -60,7 +60,7 @@ export default {
class="table-mobile-header" class="table-mobile-header"
role="rowheader" role="rowheader"
> >
Duration {{ s__('Pipeline|Duration') }}
</div> </div>
<div class="table-mobile-content"> <div class="table-mobile-content">
<p <p
...@@ -87,7 +87,8 @@ export default { ...@@ -87,7 +87,8 @@ export default {
v-tooltip v-tooltip
:title="tooltipTitle(finishedTime)" :title="tooltipTitle(finishedTime)"
data-placement="top" data-placement="top"
data-container="body"> data-container="body"
>
{{ timeFormated(finishedTime) }} {{ timeFormated(finishedTime) }}
</time> </time>
</p> </p>
......
...@@ -4,8 +4,10 @@ import { slugifyWithHyphens } from '../lib/utils/text_utility'; ...@@ -4,8 +4,10 @@ import { slugifyWithHyphens } from '../lib/utils/text_utility';
let hasUserDefinedProjectPath = false; let hasUserDefinedProjectPath = false;
const deriveProjectPathFromUrl = ($projectImportUrl) => { const deriveProjectPathFromUrl = $projectImportUrl => {
const $currentProjectPath = $projectImportUrl.parents('.toggle-import-form').find('#project_path'); const $currentProjectPath = $projectImportUrl
.parents('.toggle-import-form')
.find('#project_path');
if (hasUserDefinedProjectPath) { if (hasUserDefinedProjectPath) {
return; return;
} }
...@@ -52,9 +54,11 @@ const bindEvents = () => { ...@@ -52,9 +54,11 @@ const bindEvents = () => {
return; return;
} }
$('.how_to_import_link').on('click', (e) => { $('.how_to_import_link').on('click', e => {
e.preventDefault(); e.preventDefault();
$(e.currentTarget).next('.modal').show(); $(e.currentTarget)
.next('.modal')
.show();
}); });
$('.modal-header .close').on('click', () => { $('.modal-header .close').on('click', () => {
...@@ -63,15 +67,21 @@ const bindEvents = () => { ...@@ -63,15 +67,21 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').on('click', () => { $('.btn_import_gitlab_project').on('click', () => {
const importHref = $('a.btn_import_gitlab_project').attr('href'); const importHref = $('a.btn_import_gitlab_project').attr('href');
$('.btn_import_gitlab_project') $('.btn_import_gitlab_project').attr(
.attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&name=${$projectName.val()}&path=${$projectPath.val()}`); 'href',
`${importHref}?namespace_id=${$(
'#project_namespace_id',
).val()}&name=${$projectName.val()}&path=${$projectPath.val()}`,
);
}); });
if ($pushNewProjectTipTrigger) { if ($pushNewProjectTipTrigger) {
$pushNewProjectTipTrigger $pushNewProjectTipTrigger
.removeAttr('rel') .removeAttr('rel')
.removeAttr('target') .removeAttr('target')
.on('click', (e) => { e.preventDefault(); }) .on('click', e => {
e.preventDefault();
})
.popover({ .popover({
title: $pushNewProjectTipTrigger.data('title'), title: $pushNewProjectTipTrigger.data('title'),
placement: 'bottom', placement: 'bottom',
...@@ -79,13 +89,15 @@ const bindEvents = () => { ...@@ -79,13 +89,15 @@ const bindEvents = () => {
content: $('.push-new-project-tip-template').html(), content: $('.push-new-project-tip-template').html(),
}) })
.on('shown.bs.popover', () => { .on('shown.bs.popover', () => {
$(document).on('click.popover touchstart.popover', (event) => { $(document).on('click.popover touchstart.popover', event => {
if ($(event.target).closest('.popover').length === 0) { if ($(event.target).closest('.popover').length === 0) {
$pushNewProjectTipTrigger.trigger('click'); $pushNewProjectTipTrigger.trigger('click');
} }
}); });
const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find('.js-select-on-focus'); const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find(
'.js-select-on-focus',
);
addSelectOnFocusBehaviour(target); addSelectOnFocusBehaviour(target);
target.focus(); target.focus();
...@@ -117,13 +129,15 @@ const bindEvents = () => { ...@@ -117,13 +129,15 @@ const bindEvents = () => {
const selectedTemplate = templates[value]; const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text); $selectedTemplateText.text(selectedTemplate.text);
$(selectedTemplate.icon).clone().addClass('d-block').appendTo($selectedIcon); $(selectedTemplate.icon)
.clone()
.addClass('d-block')
.appendTo($selectedIcon);
const $activeTabProjectName = $('.tab-pane.active #project_name'); const $activeTabProjectName = $('.tab-pane.active #project_name');
const $activeTabProjectPath = $('.tab-pane.active #project_path'); const $activeTabProjectPath = $('.tab-pane.active #project_path');
$activeTabProjectName.focus(); $activeTabProjectName.focus();
$activeTabProjectName $activeTabProjectName.keyup(() => {
.keyup(() => {
onProjectNameChange($activeTabProjectName, $activeTabProjectPath); onProjectNameChange($activeTabProjectName, $activeTabProjectPath);
hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0; hasUserDefinedProjectPath = $activeTabProjectPath.val().trim().length > 0;
}); });
......
...@@ -21,7 +21,7 @@ Sidebar.initialize = function(currentUser) { ...@@ -21,7 +21,7 @@ Sidebar.initialize = function(currentUser) {
} }
}; };
Sidebar.prototype.removeListeners = function () { Sidebar.prototype.removeListeners = function() {
this.sidebar.off('click', '.sidebar-collapsed-icon'); this.sidebar.off('click', '.sidebar-collapsed-icon');
this.sidebar.off('hidden.gl.dropdown'); this.sidebar.off('hidden.gl.dropdown');
$('.dropdown').off('loading.gl.dropdown'); $('.dropdown').off('loading.gl.dropdown');
...@@ -38,10 +38,12 @@ Sidebar.prototype.addEventListeners = function() { ...@@ -38,10 +38,12 @@ Sidebar.prototype.addEventListeners = function() {
$('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
$document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked); $document.on('click', '.js-sidebar-toggle', this.sidebarToggleClicked);
return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); return $(document)
.off('click', '.js-issuable-todo')
.on('click', '.js-issuable-todo', this.toggleTodo);
}; };
Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { Sidebar.prototype.sidebarToggleClicked = function(e, triggered) {
var $allGutterToggleIcons, $this, isExpanded, tooltipLabel; var $allGutterToggleIcons, $this, isExpanded, tooltipLabel;
e.preventDefault(); e.preventDefault();
$this = $(this); $this = $(this);
...@@ -51,18 +53,26 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { ...@@ -51,18 +53,26 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) {
if (isExpanded) { if (isExpanded) {
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
$('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); $('aside.right-sidebar')
$('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); .removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed');
$('.layout-page')
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed');
} else { } else {
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); $allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
$('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); $('aside.right-sidebar')
$('.layout-page').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); .removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded');
$('.layout-page')
.removeClass('right-sidebar-collapsed')
.addClass('right-sidebar-expanded');
} }
$this.attr('data-original-title', tooltipLabel); $this.attr('data-original-title', tooltipLabel);
if (!triggered) { if (!triggered) {
Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); Cookies.set('collapsed_gutter', $('.right-sidebar').hasClass('right-sidebar-collapsed'));
} }
}; };
...@@ -71,21 +81,27 @@ Sidebar.prototype.toggleTodo = function(e) { ...@@ -71,21 +81,27 @@ Sidebar.prototype.toggleTodo = function(e) {
$this = $(e.currentTarget); $this = $(e.currentTarget);
ajaxType = $this.attr('data-delete-path') ? 'delete' : 'post'; ajaxType = $this.attr('data-delete-path') ? 'delete' : 'post';
if ($this.attr('data-delete-path')) { if ($this.attr('data-delete-path')) {
url = "" + ($this.attr('data-delete-path')); url = '' + $this.attr('data-delete-path');
} else { } else {
url = "" + ($this.data('url')); url = '' + $this.data('url');
} }
$this.tooltip('hide'); $this.tooltip('hide');
$('.js-issuable-todo').disable().addClass('is-loading'); $('.js-issuable-todo')
.disable()
.addClass('is-loading');
axios[ajaxType](url, { axios[ajaxType](url, {
issuable_id: $this.data('issuableId'), issuable_id: $this.data('issuableId'),
issuable_type: $this.data('issuableType'), issuable_type: $this.data('issuableType'),
}).then(({ data }) => { })
.then(({ data }) => {
this.todoUpdateDone(data); this.todoUpdateDone(data);
}).catch(() => flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`)); })
.catch(() =>
flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`),
);
}; };
Sidebar.prototype.todoUpdateDone = function(data) { Sidebar.prototype.todoUpdateDone = function(data) {
...@@ -99,7 +115,8 @@ Sidebar.prototype.todoUpdateDone = function(data) { ...@@ -99,7 +115,8 @@ Sidebar.prototype.todoUpdateDone = function(data) {
const $el = $(el); const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner'); const $elText = $el.find('.js-issuable-todo-inner');
$el.removeClass('is-loading') $el
.removeClass('is-loading')
.enable() .enable()
.attr('aria-label', $el.data(`${attrPrefix}Text`)) .attr('aria-label', $el.data(`${attrPrefix}Text`))
.attr('data-delete-path', deletePath) .attr('data-delete-path', deletePath)
...@@ -119,7 +136,9 @@ Sidebar.prototype.todoUpdateDone = function(data) { ...@@ -119,7 +136,9 @@ Sidebar.prototype.todoUpdateDone = function(data) {
Sidebar.prototype.sidebarDropdownLoading = function(e) { Sidebar.prototype.sidebarDropdownLoading = function(e) {
var $loading, $sidebarCollapsedIcon, i, img; var $loading, $sidebarCollapsedIcon, i, img;
$sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); $sidebarCollapsedIcon = $(this)
.closest('.block')
.find('.sidebar-collapsed-icon');
img = $sidebarCollapsedIcon.find('img'); img = $sidebarCollapsedIcon.find('img');
i = $sidebarCollapsedIcon.find('i'); i = $sidebarCollapsedIcon.find('i');
$loading = $('<i class="fa fa-spinner fa-spin"></i>'); $loading = $('<i class="fa fa-spinner fa-spin"></i>');
...@@ -134,7 +153,9 @@ Sidebar.prototype.sidebarDropdownLoading = function(e) { ...@@ -134,7 +153,9 @@ Sidebar.prototype.sidebarDropdownLoading = function(e) {
Sidebar.prototype.sidebarDropdownLoaded = function(e) { Sidebar.prototype.sidebarDropdownLoaded = function(e) {
var $sidebarCollapsedIcon, i, img; var $sidebarCollapsedIcon, i, img;
$sidebarCollapsedIcon = $(this).closest('.block').find('.sidebar-collapsed-icon'); $sidebarCollapsedIcon = $(this)
.closest('.block')
.find('.sidebar-collapsed-icon');
img = $sidebarCollapsedIcon.find('img'); img = $sidebarCollapsedIcon.find('img');
$sidebarCollapsedIcon.find('i.fa-spin').remove(); $sidebarCollapsedIcon.find('i.fa-spin').remove();
i = $sidebarCollapsedIcon.find('i'); i = $sidebarCollapsedIcon.find('i');
...@@ -220,7 +241,7 @@ Sidebar.prototype.isOpen = function() { ...@@ -220,7 +241,7 @@ Sidebar.prototype.isOpen = function() {
}; };
Sidebar.prototype.getBlock = function(name) { Sidebar.prototype.getBlock = function(name) {
return this.sidebar.find(".block." + name); return this.sidebar.find('.block.' + name);
}; };
export default Sidebar; export default Sidebar;
...@@ -226,7 +226,7 @@ export class SearchAutocomplete { ...@@ -226,7 +226,7 @@ export class SearchAutocomplete {
icon, icon,
text: term, text: term,
template: s__('SearchAutocomplete|in all GitLab'), template: s__('SearchAutocomplete|in all GitLab'),
url: `/search?search=${term}`, url: `${gon.relative_url_root}/search?search=${term}`,
}); });
if (template) { if (template) {
...@@ -234,7 +234,9 @@ export class SearchAutocomplete { ...@@ -234,7 +234,9 @@ export class SearchAutocomplete {
icon, icon,
text: term, text: term,
template, template,
url: `/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`, url: `${
gon.relative_url_root
}/search?search=${term}&project_id=${this.projectInputEl.val()}&group_id=${this.groupInputEl.val()}`,
}); });
} }
} }
......
...@@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue'; ...@@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import GfmAutoComplete from '~/gfm_auto_complete'; import GfmAutoComplete from '~/gfm_auto_complete';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import Api from '~/api'; import Api from '~/api';
import { GlModal } from '@gitlab-org/gitlab-ui';
import eventHub from './event_hub'; import eventHub from './event_hub';
import EmojiMenuInModal from './emoji_menu_in_modal'; import EmojiMenuInModal from './emoji_menu_in_modal';
...@@ -13,6 +14,7 @@ const emojiMenuClass = 'js-modal-status-emoji-menu'; ...@@ -13,6 +14,7 @@ const emojiMenuClass = 'js-modal-status-emoji-menu';
export default { export default {
components: { components: {
Icon, Icon,
GlModal,
}, },
props: { props: {
currentEmoji: { currentEmoji: {
...@@ -152,7 +154,7 @@ export default { ...@@ -152,7 +154,7 @@ export default {
</script> </script>
<template> <template>
<gl-ui-modal <gl-modal
:title="s__('SetStatusModal|Set a status')" :title="s__('SetStatusModal|Set a status')"
:modal-id="modalId" :modal-id="modalId"
:ok-title="s__('SetStatusModal|Set status')" :ok-title="s__('SetStatusModal|Set status')"
...@@ -237,5 +239,5 @@ export default { ...@@ -237,5 +239,5 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</gl-ui-modal> </gl-modal>
</template> </template>
...@@ -74,8 +74,8 @@ export default { ...@@ -74,8 +74,8 @@ export default {
} }
if (!this.users.length) { if (!this.users.length) {
const emptyTooltipLabel = this.issuableType === 'issue' ? const emptyTooltipLabel =
__('Assignee(s)') : __('Assignee'); this.issuableType === 'issue' ? __('Assignee(s)') : __('Assignee');
names.push(emptyTooltipLabel); names.push(emptyTooltipLabel);
} }
...@@ -248,4 +248,3 @@ export default { ...@@ -248,4 +248,3 @@ export default {
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { __ } from '~/locale'; import { __ } from '~/locale';
import icon from '~/vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import toggleButton from '~/vue_shared/components/toggle_button.vue'; import toggleButton from '~/vue_shared/components/toggle_button.vue';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
const ICON_ON = 'notifications'; const ICON_ON = 'notifications';
const ICON_OFF = 'notifications-off'; const ICON_OFF = 'notifications-off';
const LABEL_ON = __('Notifications on'); const LABEL_ON = __('Notifications on');
const LABEL_OFF = __('Notifications off'); const LABEL_OFF = __('Notifications off');
export default { export default {
directives: { directives: {
tooltip, tooltip,
}, },
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
this.$emit('toggleSidebar'); this.$emit('toggleSidebar');
}, },
}, },
}; };
</script> </script>
<template> <template>
......
...@@ -39,9 +39,10 @@ export default class SidebarMediator { ...@@ -39,9 +39,10 @@ export default class SidebarMediator {
} }
fetch() { fetch() {
return this.service.get() return this.service
.get()
.then(response => response.json()) .then(response => response.json())
.then((data) => { .then(data => {
this.processFetchedData(data); this.processFetchedData(data);
}) })
.catch(() => new Flash('Error occurred when fetching sidebar data')); .catch(() => new Flash('Error occurred when fetching sidebar data'));
...@@ -56,30 +57,33 @@ export default class SidebarMediator { ...@@ -56,30 +57,33 @@ export default class SidebarMediator {
toggleSubscription() { toggleSubscription() {
this.store.setFetchingState('subscriptions', true); this.store.setFetchingState('subscriptions', true);
return this.service.toggleSubscription() return this.service
.toggleSubscription()
.then(() => { .then(() => {
this.store.setSubscribedState(!this.store.subscribed); this.store.setSubscribedState(!this.store.subscribed);
this.store.setFetchingState('subscriptions', false); this.store.setFetchingState('subscriptions', false);
}) })
.catch((err) => { .catch(err => {
this.store.setFetchingState('subscriptions', false); this.store.setFetchingState('subscriptions', false);
throw err; throw err;
}); });
} }
fetchAutocompleteProjects(searchTerm) { fetchAutocompleteProjects(searchTerm) {
return this.service.getProjectsAutocomplete(searchTerm) return this.service
.getProjectsAutocomplete(searchTerm)
.then(response => response.json()) .then(response => response.json())
.then((data) => { .then(data => {
this.store.setAutocompleteProjects(data); this.store.setAutocompleteProjects(data);
return this.store.autocompleteProjects; return this.store.autocompleteProjects;
}); });
} }
moveIssue() { moveIssue() {
return this.service.moveIssue(this.store.moveToProjectId) return this.service
.moveIssue(this.store.moveToProjectId)
.then(response => response.json()) .then(response => response.json())
.then((data) => { .then(data => {
if (window.location.pathname !== data.web_url) { if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url); visitUrl(data.web_url);
} }
......
<script> <script>
/* eslint-disable vue/require-default-prop */ /* eslint-disable vue/require-default-prop */
import { sprintf, __ } from '~/locale';
import PipelineStage from '~/pipelines/components/stage.vue'; import PipelineStage from '~/pipelines/components/stage.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -36,6 +37,10 @@ export default { ...@@ -36,6 +37,10 @@ export default {
type: String, type: String,
required: false, required: false,
}, },
troubleshootingDocsPath: {
type: String,
required: true,
},
}, },
computed: { computed: {
hasPipeline() { hasPipeline() {
...@@ -57,6 +62,17 @@ export default { ...@@ -57,6 +62,17 @@ export default {
hasCommitInfo() { hasCommitInfo() {
return this.pipeline.commit && Object.keys(this.pipeline.commit).length > 0; return this.pipeline.commit && Object.keys(this.pipeline.commit).length > 0;
}, },
errorText() {
return sprintf(
__(
'Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}',
),
{
linkStart: `<a href="${this.troubleshootingDocsPath}">`,
linkEnd: '</a>',
},
);
},
}, },
}; };
</script> </script>
...@@ -77,8 +93,10 @@ export default { ...@@ -77,8 +93,10 @@ export default {
name="status_failed_borderless" name="status_failed_borderless"
/> />
</div> </div>
<div class="media-body"> <div
Could not connect to the CI server. Please check your settings and try again class="media-body"
v-html="errorText"
>
</div> </div>
</template> </template>
<template v-else-if="hasPipeline"> <template v-else-if="hasPipeline">
......
...@@ -71,7 +71,12 @@ export default { ...@@ -71,7 +71,12 @@ export default {
return defaultClass; return defaultClass;
}, },
iconClass() { iconClass() {
if (this.status === 'failed' || !this.commitMessage.length || !this.mr.isMergeAllowed || this.mr.preventMerge) { if (
this.status === 'failed' ||
!this.commitMessage.length ||
!this.mr.isMergeAllowed ||
this.mr.preventMerge
) {
return 'warning'; return 'warning';
} }
return 'success'; return 'success';
...@@ -90,10 +95,12 @@ export default { ...@@ -90,10 +95,12 @@ export default {
}, },
isMergeButtonDisabled() { isMergeButtonDisabled() {
const { commitMessage } = this; const { commitMessage } = this;
return Boolean(!commitMessage.length return Boolean(
|| !this.shouldShowMergeControls() !commitMessage.length ||
|| this.isMakingRequest !this.shouldShowMergeControls() ||
|| this.mr.preventMerge); this.isMakingRequest ||
this.mr.preventMerge,
);
}, },
isRemoveSourceBranchButtonDisabled() { isRemoveSourceBranchButtonDisabled() {
return this.isMergeButtonDisabled; return this.isMergeButtonDisabled;
...@@ -140,9 +147,10 @@ export default { ...@@ -140,9 +147,10 @@ export default {
}; };
this.isMakingRequest = true; this.isMakingRequest = true;
this.service.merge(options) this.service
.merge(options)
.then(res => res.data) .then(res => res.data)
.then((data) => { .then(data => {
const hasError = data.status === 'failed' || data.status === 'hook_validation_error'; const hasError = data.status === 'failed' || data.status === 'hook_validation_error';
if (data.status === 'merge_when_pipeline_succeeds') { if (data.status === 'merge_when_pipeline_succeeds') {
...@@ -167,9 +175,10 @@ export default { ...@@ -167,9 +175,10 @@ export default {
}); });
}, },
handleMergePolling(continuePolling, stopPolling) { handleMergePolling(continuePolling, stopPolling) {
this.service.poll() this.service
.poll()
.then(res => res.data) .then(res => res.data)
.then((data) => { .then(data => {
if (data.state === 'merged') { if (data.state === 'merged') {
// If state is merged we should update the widget and stop the polling // If state is merged we should update the widget and stop the polling
eventHub.$emit('MRWidgetUpdateRequested'); eventHub.$emit('MRWidgetUpdateRequested');
...@@ -205,9 +214,10 @@ export default { ...@@ -205,9 +214,10 @@ export default {
}); });
}, },
handleRemoveBranchPolling(continuePolling, stopPolling) { handleRemoveBranchPolling(continuePolling, stopPolling) {
this.service.poll() this.service
.poll()
.then(res => res.data) .then(res => res.data)
.then((data) => { .then(data => {
// If source branch exists then we should continue polling // If source branch exists then we should continue polling
// because removing a source branch is a background task and takes time // because removing a source branch is a background task and takes time
if (data.source_branch_exists) { if (data.source_branch_exists) {
......
...@@ -116,7 +116,7 @@ export default { ...@@ -116,7 +116,7 @@ export default {
// init polling // init polling
this.initPostMergeDeploymentsPolling(); this.initPostMergeDeploymentsPolling();
} }
} },
}, },
created() { created() {
this.initPolling(); this.initPolling();
...@@ -213,7 +213,7 @@ export default { ...@@ -213,7 +213,7 @@ export default {
}) })
.catch(() => this.throwDeploymentsError()); .catch(() => this.throwDeploymentsError());
}, },
fetchPostMergeDeployments(){ fetchPostMergeDeployments() {
return this.fetchDeployments('merge_commit') return this.fetchDeployments('merge_commit')
.then(({ data }) => { .then(({ data }) => {
if (data.length) { if (data.length) {
...@@ -223,7 +223,11 @@ export default { ...@@ -223,7 +223,11 @@ export default {
.catch(() => this.throwDeploymentsError()); .catch(() => this.throwDeploymentsError());
}, },
throwDeploymentsError() { throwDeploymentsError() {
createFlash(__('Something went wrong while fetching the environments for this merge request. Please try again.')); createFlash(
__(
'Something went wrong while fetching the environments for this merge request. Please try again.',
),
);
}, },
fetchActionsContent() { fetchActionsContent() {
this.service this.service
...@@ -301,6 +305,7 @@ export default { ...@@ -301,6 +305,7 @@ export default {
:has-ci="mr.hasCI" :has-ci="mr.hasCI"
:source-branch="mr.sourceBranch" :source-branch="mr.sourceBranch"
:source-branch-link="mr.sourceBranchLink" :source-branch-link="mr.sourceBranchLink"
:troubleshooting-docs-path="mr.troubleshootingDocsPath"
/> />
<deployment <deployment
v-for="deployment in mr.deployments" v-for="deployment in mr.deployments"
...@@ -355,6 +360,7 @@ export default { ...@@ -355,6 +360,7 @@ export default {
:has-ci="mr.hasCI" :has-ci="mr.hasCI"
:source-branch="mr.targetBranch" :source-branch="mr.targetBranch"
:source-branch-link="mr.targetBranch" :source-branch-link="mr.targetBranch"
:troubleshooting-docs-path="mr.troubleshootingDocsPath"
/> />
<deployment <deployment
v-for="postMergeDeployment in mr.postMergeDeployments" v-for="postMergeDeployment in mr.postMergeDeployments"
......
...@@ -24,8 +24,8 @@ export default class MRWidgetService { ...@@ -24,8 +24,8 @@ export default class MRWidgetService {
fetchDeployments(targetParam) { fetchDeployments(targetParam) {
return axios.get(this.endpoints.ciEnvironmentsStatusPath, { return axios.get(this.endpoints.ciEnvironmentsStatusPath, {
params: { params: {
environment_target: targetParam environment_target: targetParam,
} },
}); });
} }
......
...@@ -18,6 +18,7 @@ export default class MergeRequestStore { ...@@ -18,6 +18,7 @@ export default class MergeRequestStore {
this.squash = data.squash; this.squash = data.squash;
this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath =
this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path; this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path;
this.troubleshootingDocsPath = this.troubleshootingDocsPath || data.troubleshooting_docs_path;
this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true; this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
this.iid = data.iid; this.iid = data.iid;
......
...@@ -13,7 +13,7 @@ export default { ...@@ -13,7 +13,7 @@ export default {
}, },
props: { props: {
/** /**
* Indicates the existance of a tag. * Indicates the existence of a tag.
* Used to render the correct icon, if true will render `fa-tag` icon, * Used to render the correct icon, if true will render `fa-tag` icon,
* if false will render a svg sprite fork icon * if false will render a svg sprite fork icon
*/ */
......
...@@ -56,12 +56,14 @@ export default { ...@@ -56,12 +56,14 @@ export default {
filteredResults() { filteredResults() {
if (this.filter !== '') { if (this.filter !== '') {
return this.items.filter( return this.items.filter(
item => item[this.filterKey] && item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()), item =>
item[this.filterKey] &&
item[this.filterKey].toLowerCase().includes(this.filter.toLowerCase()),
); );
} }
return this.items.slice(0, this.visibleItems); return this.items.slice(0, this.visibleItems);
} },
}, },
mounted() { mounted() {
/** /**
......
...@@ -8,7 +8,7 @@ let iconValidator = () => true; ...@@ -8,7 +8,7 @@ let iconValidator = () => true;
*/ */
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line global-require // eslint-disable-next-line global-require
const data = require('@gitlab-org/gitlab-svgs/dist/icons.json'); const data = require('@gitlab/svgs/dist/icons.json');
const { icons } = data; const { icons } = data;
iconValidator = value => { iconValidator = value => {
if (icons.includes(value)) { if (icons.includes(value)) {
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import Tooltip from '../../directives/tooltip'; import { GlTooltipDirective } from '@gitlab-org/gitlab-ui';
import ToolbarButton from './toolbar_button.vue'; import ToolbarButton from './toolbar_button.vue';
import Icon from '../icon.vue'; import Icon from '../icon.vue';
export default { export default {
directives: {
Tooltip,
},
components: { components: {
ToolbarButton, ToolbarButton,
Icon, Icon,
}, },
directives: {
GlTooltip: GlTooltipDirective,
},
props: { props: {
previewMarkdown: { previewMarkdown: {
type: Boolean, type: Boolean,
...@@ -147,7 +147,7 @@ export default { ...@@ -147,7 +147,7 @@ export default {
icon="table" icon="table"
/> />
<button <button
v-tooltip v-gl-tooltip
aria-label="Go full screen" aria-label="Go full screen"
class="toolbar-btn toolbar-fullscreen-btn js-zen-enter" class="toolbar-btn toolbar-fullscreen-btn js-zen-enter"
data-container="body" data-container="body"
......
<script> <script>
import tooltip from '../../directives/tooltip'; import { GlTooltipDirective } from '@gitlab-org/gitlab-ui';
import icon from '../icon.vue'; import Icon from '../icon.vue';
export default { export default {
components: { components: {
icon, Icon,
}, },
directives: { directives: {
tooltip, GlTooltip: GlTooltipDirective,
}, },
props: { props: {
buttonTitle: { buttonTitle: {
...@@ -43,7 +43,7 @@ export default { ...@@ -43,7 +43,7 @@ export default {
<template> <template>
<button <button
v-tooltip v-gl-tooltip
:data-md-tag="tag" :data-md-tag="tag"
:data-md-select="tagSelect" :data-md-select="tagSelect"
:data-md-block="tagBlock" :data-md-block="tagBlock"
......
<script> <script>
import { GlPagination } from '@gitlab-org/gitlab-ui';
import { s__ } from '../../locale'; import { s__ } from '../../locale';
export default { export default {
components: {
GlPagination,
},
props: { props: {
change: { change: {
type: Function, type: Function,
......
<script> <script>
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
export default { export default {
name: 'CollapsedCalendarIcon', name: 'CollapsedCalendarIcon',
directives: { directives: {
tooltip, tooltip,
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
this.$emit('click'); this.$emit('click');
}, },
}, },
}; };
</script> </script>
<template> <template>
......
<script> <script>
import { __ } from '~/locale'; import { __ } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago'; import timeagoMixin from '~/vue_shared/mixins/timeago';
import { dateInWords, timeFor } from '~/lib/utils/datetime_utility'; import { dateInWords, timeFor } from '~/lib/utils/datetime_utility';
import collapsedCalendarIcon from './collapsed_calendar_icon.vue'; import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
export default { export default {
name: 'SidebarCollapsedGroupedDatePicker', name: 'SidebarCollapsedGroupedDatePicker',
components: { components: {
collapsedCalendarIcon, collapsedCalendarIcon,
}, },
mixins: [ mixins: [timeagoMixin],
timeagoMixin,
],
props: { props: {
collapsed: { collapsed: {
type: Boolean, type: Boolean,
...@@ -67,10 +65,7 @@ ...@@ -67,10 +65,7 @@
const defaultText = dateType === 'min' ? __('Start date') : __('Due date'); const defaultText = dateType === 'min' ? __('Start date') : __('Due date');
const date = this[`${dateType}Date`]; const date = this[`${dateType}Date`];
const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date); const timeAgo = dateType === 'min' ? this.timeFormated(date) : timeFor(date);
const dateText = date ? [ const dateText = date ? [this.dateText(dateType), `(${timeAgo})`].join(' ') : '';
this.dateText(dateType),
`(${timeAgo})`,
].join(' ') : '';
if (date) { if (date) {
return [defaultText, dateText].join('<br />'); return [defaultText, dateText].join('<br />');
...@@ -78,7 +73,7 @@ ...@@ -78,7 +73,7 @@
return __('Start and due date'); return __('Start and due date');
}, },
}, },
}; };
</script> </script>
<template> <template>
......
...@@ -14,7 +14,10 @@ export default { ...@@ -14,7 +14,10 @@ export default {
}, },
computed: { computed: {
labelsList() { labelsList() {
const labelsString = this.labels.slice(0, 5).map(label => label.title).join(', '); const labelsString = this.labels
.slice(0, 5)
.map(label => label.title)
.join(', ');
if (this.labels.length > 5) { if (this.labels.length > 5) {
return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), { return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
......
...@@ -158,7 +158,7 @@ ...@@ -158,7 +158,7 @@
color: $gl-text-color; color: $gl-text-color;
outline: 0; outline: 0;
// make sure the text color is not overriden // make sure the text color is not overridden
&.text-danger { &.text-danger {
color: $brand-danger; color: $brand-danger;
} }
...@@ -184,7 +184,7 @@ ...@@ -184,7 +184,7 @@
text-align: left; text-align: left;
width: 100%; width: 100%;
// make sure the text color is not overriden // make sure the text color is not overridden
&.text-danger { &.text-danger {
color: $brand-danger; color: $brand-danger;
} }
......
// For tabbed navigation links, scrolling tabs, etc. For all top/main navigation, // For tabbed navigation links, scrolling tabs, etc. For all top/main navigation,
// please check nav.scss // please check nav.scss
.nav-links { .nav-links:not(.quick-links) {
display: flex; display: flex;
padding: 0; padding: 0;
margin: 0; margin: 0;
...@@ -106,7 +106,7 @@ ...@@ -106,7 +106,7 @@
display: inline-block; display: inline-block;
float: right; float: right;
text-align: right; text-align: right;
padding: 11px 0; padding: $gl-padding-8 0;
margin-bottom: 0; margin-bottom: 0;
> .btn, > .btn,
......
...@@ -33,21 +33,21 @@ class Admin::AppearancesController < Admin::ApplicationController ...@@ -33,21 +33,21 @@ class Admin::AppearancesController < Admin::ApplicationController
@appearance.save @appearance.save
redirect_to admin_appearances_path, notice: 'Logo was succesfully removed.' redirect_to admin_appearances_path, notice: 'Logo was successfully removed.'
end end
def header_logos def header_logos
@appearance.remove_header_logo! @appearance.remove_header_logo!
@appearance.save @appearance.save
redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.' redirect_to admin_appearances_path, notice: 'Header logo was successfully removed.'
end end
def favicon def favicon
@appearance.remove_favicon! @appearance.remove_favicon!
@appearance.save @appearance.save
redirect_to admin_appearances_path, notice: 'Favicon was succesfully removed.' redirect_to admin_appearances_path, notice: 'Favicon was successfully removed.'
end end
private private
......
...@@ -61,7 +61,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -61,7 +61,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
format.html do format.html do
usage_data_json = JSON.pretty_generate(Gitlab::UsageData.data) usage_data_json = JSON.pretty_generate(Gitlab::UsageData.data)
render html: Gitlab::Highlight.highlight('payload.json', usage_data_json) render html: Gitlab::Highlight.highlight('payload.json', usage_data_json, language: 'json')
end end
format.json { render json: Gitlab::UsageData.to_json } format.json { render json: Gitlab::UsageData.to_json }
end end
......
...@@ -23,7 +23,7 @@ class Import::GiteaController < Import::GithubController ...@@ -23,7 +23,7 @@ class Import::GiteaController < Import::GithubController
:"#{provider}_host_url" :"#{provider}_host_url"
end end
# Overriden methods # Overridden methods
def provider def provider
:gitea :gitea
end end
......
...@@ -103,7 +103,7 @@ class Import::GithubController < Import::BaseController ...@@ -103,7 +103,7 @@ class Import::GithubController < Import::BaseController
{ github_access_token: session[access_token_key] } { github_access_token: session[access_token_key] }
end end
# The following methods are overriden in subclasses # The following methods are overridden in subclasses
def provider def provider
:github :github
end end
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
layout 'profile' layout 'profile'
# Overriden from Doorkeeper::AuthorizationsController to # Overridden from Doorkeeper::AuthorizationsController to
# include the call to session.delete # include the call to session.delete
def new def new
if pre_auth.authorizable? if pre_auth.authorizable?
......
...@@ -92,7 +92,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -92,7 +92,7 @@ class Projects::BlobController < Projects::ApplicationController
apply_diff_view_cookie! apply_diff_view_cookie!
@blob.load_all_data! @blob.load_all_data!
@lines = Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: @repository).lines @lines = @blob.present.highlight.lines
@form = UnfoldForm.new(params) @form = UnfoldForm.new(params)
......
...@@ -276,7 +276,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -276,7 +276,7 @@ class ProjectsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
# Render project landing depending of which features are available # Render project landing depending of which features are available
# So if page is not availble in the list it renders the next page # So if page is not available in the list it renders the next page
# #
# pages list order: repository readme, wiki home, issues list, customize workflow # pages list order: repository readme, wiki home, issues list, customize workflow
def render_landing_page def render_landing_page
......
...@@ -72,7 +72,6 @@ module Autocomplete ...@@ -72,7 +72,6 @@ module Autocomplete
author_id.present? && current_user author_id.present? && current_user
end end
# rubocop: disable CodeReuse/ActiveRecord
def find_users def find_users
if project if project
project.authorized_users.union_with_user(author_id) project.authorized_users.union_with_user(author_id)
...@@ -84,6 +83,5 @@ module Autocomplete ...@@ -84,6 +83,5 @@ module Autocomplete
User.none User.none
end end
end end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
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.
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