Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-08 15:09:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-08 15:09:33 +0300
commitc52b72f5772d52e9fc85bd9f4e8b8497a6278c37 (patch)
treebbe0504b4c07a93e24db4a72785a847b2540eef8 /app/assets
parent21341457a8c422d890a9ec30838b597dea565d62 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/api.js15
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue5
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue5
-rw-r--r--app/assets/javascripts/boards/components/modal/header.vue4
-rw-r--r--app/assets/javascripts/boards/components/modal/index.vue10
-rw-r--r--app/assets/javascripts/boards/mount_multiple_boards_switcher.js2
-rw-r--r--app/assets/javascripts/milestone_select.js151
-rw-r--r--app/assets/javascripts/static_site_editor/components/edit_area.vue19
-rw-r--r--app/assets/javascripts/static_site_editor/constants.js2
-rw-r--r--app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js4
-rw-r--r--app/assets/javascripts/static_site_editor/image_repository.js20
-rw-r--r--app/assets/javascripts/static_site_editor/pages/home.vue7
-rw-r--r--app/assets/javascripts/static_site_editor/services/image_service.js9
-rw-r--r--app/assets/javascripts/static_site_editor/services/submit_content_changes.js32
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue9
-rw-r--r--app/assets/javascripts/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/rich_content_editor/services/image_service.js2
-rw-r--r--app/assets/stylesheets/framework/system_messages.scss3
-rw-r--r--app/assets/stylesheets/pages/boards.scss26
-rw-r--r--app/assets/stylesheets/utilities.scss9
23 files changed, 232 insertions, 130 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 94d155840ea..460611356c0 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -9,6 +9,7 @@ const Api = {
groupsPath: '/api/:version/groups.json',
groupPath: '/api/:version/groups/:id',
groupMembersPath: '/api/:version/groups/:id/members',
+ groupMilestonesPath: '/api/:version/groups/:id/milestones',
subgroupsPath: '/api/:version/groups/:id/subgroups',
namespacesPath: '/api/:version/namespaces.json',
groupProjectsPath: '/api/:version/groups/:id/projects.json',
@@ -98,6 +99,14 @@ const Api = {
return axios.get(url).then(({ data }) => data);
},
+ groupMilestones(groupId, params = {}) {
+ const url = Api.buildUrl(Api.groupMilestonesPath).replace(':id', encodeURIComponent(groupId));
+
+ return axios.get(url, {
+ params,
+ });
+ },
+
// Return namespaces list. Filtered by query
namespaces(query, callback) {
const url = Api.buildUrl(Api.namespacesPath);
@@ -262,10 +271,12 @@ const Api = {
});
},
- projectMilestones(id) {
+ projectMilestones(id, params = {}) {
const url = Api.buildUrl(Api.projectMilestonesPath).replace(':id', encodeURIComponent(id));
- return axios.get(url);
+ return axios.get(url, {
+ params,
+ });
},
mergeRequests(params = {}) {
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index f0497ea0b64..6d0235aeaae 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -54,7 +54,7 @@ export default {
<div>
<div
v-if="!isSwimlanesOn"
- class="boards-list w-100 py-3 px-2 text-nowrap"
+ class="boards-list gl-w-full gl-py-5 gl-px-3 gl-white-space-nowrap"
data-qa-selector="boards_list"
>
<board-column
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index fbe221041c1..8ff493ae8b1 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -25,10 +25,6 @@ export default {
type: Boolean,
required: true,
},
- milestonePath: {
- type: String,
- required: true,
- },
labelsPath: {
type: String,
required: true,
@@ -201,7 +197,6 @@ export default {
:collapse-scope="isNewForm"
:board="board"
:can-admin-board="canAdminBoard"
- :milestone-path="milestonePath"
:labels-path="labelsPath"
:enable-scoped-labels="enableScopedLabels"
:project-id="projectId"
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index 80db9930259..56fb4f1a6c4 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -36,10 +36,6 @@ export default {
type: Object,
required: true,
},
- milestonePath: {
- type: String,
- required: true,
- },
throttleDuration: {
type: Number,
default: 200,
@@ -335,7 +331,6 @@ export default {
<board-form
v-if="currentPage"
- :milestone-path="milestonePath"
:labels-path="labelsPath"
:project-id="projectId"
:group-id="groupId"
diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue
index a42e691dcf3..8b0d836f27a 100644
--- a/app/assets/javascripts/boards/components/modal/header.vue
+++ b/app/assets/javascripts/boards/components/modal/header.vue
@@ -17,10 +17,6 @@ export default {
type: Number,
required: true,
},
- milestonePath: {
- type: String,
- required: true,
- },
labelPath: {
type: String,
required: true,
diff --git a/app/assets/javascripts/boards/components/modal/index.vue b/app/assets/javascripts/boards/components/modal/index.vue
index 20344b66140..fb2d7b6dbc5 100644
--- a/app/assets/javascripts/boards/components/modal/index.vue
+++ b/app/assets/javascripts/boards/components/modal/index.vue
@@ -38,10 +38,6 @@ export default {
type: Number,
required: true,
},
- milestonePath: {
- type: String,
- required: true,
- },
labelPath: {
type: String,
required: true,
@@ -149,11 +145,7 @@ export default {
class="add-issues-modal d-flex position-fixed position-top-0 position-bottom-0 position-left-0 position-right-0 h-100"
>
<div class="add-issues-container d-flex flex-column m-auto rounded">
- <modal-header
- :project-id="projectId"
- :milestone-path="milestonePath"
- :label-path="labelPath"
- />
+ <modal-header :project-id="projectId" :label-path="labelPath" />
<modal-list
v-if="!loading && showList && !filterLoading"
:issue-link-base="issueLinkBase"
diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
index 73d37459bfe..51bb72b7657 100644
--- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
+++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
@@ -27,7 +27,7 @@ export default () => {
hasMissingBoards: parseBoolean(dataset.hasMissingBoards),
canAdminBoard: parseBoolean(dataset.canAdminBoard),
multipleIssueBoardsAvailable: parseBoolean(dataset.multipleIssueBoardsAvailable),
- projectId: Number(dataset.projectId),
+ projectId: dataset.projectId ? Number(dataset.projectId) : 0,
groupId: Number(dataset.groupId),
scopedIssueBoardFeatureEnabled: parseBoolean(dataset.scopedIssueBoardFeatureEnabled),
weights: JSON.parse(dataset.weights),
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index caa45184bfc..8213f057b0b 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -4,10 +4,11 @@
import $ from 'jquery';
import { template, escape } from 'lodash';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
import '~/gl_dropdown';
+import Api from '~/api';
import axios from './lib/utils/axios_utils';
-import { timeFor } from './lib/utils/datetime_utility';
+import { timeFor, parsePikadayDate, dateInWords } from './lib/utils/datetime_utility';
import ModalStore from './boards/stores/modal_store';
import boardsStore, {
boardStoreIssueSet,
@@ -34,10 +35,10 @@ export default class MilestoneSelect {
$els.each((i, dropdown) => {
let milestoneLinkNoneTemplate,
milestoneLinkTemplate,
+ milestoneExpiredLinkTemplate,
selectedMilestone,
selectedMilestoneDefault;
const $dropdown = $(dropdown);
- const milestonesUrl = $dropdown.data('milestones');
const issueUpdateURL = $dropdown.data('issueUpdate');
const showNo = $dropdown.data('showNo');
const showAny = $dropdown.data('showAny');
@@ -63,58 +64,101 @@ export default class MilestoneSelect {
milestoneLinkTemplate = template(
'<a href="<%- web_url %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>',
);
+ milestoneExpiredLinkTemplate = template(
+ '<a href="<%- web_url %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %> (Past due)</a>',
+ );
milestoneLinkNoneTemplate = `<span class="no-value">${__('None')}</span>`;
}
return $dropdown.glDropdown({
showMenuAbove,
- data: (term, callback) =>
- axios.get(milestonesUrl).then(({ data }) => {
- const extraOptions = [];
- if (showAny) {
- extraOptions.push({
- id: null,
- name: null,
- title: __('Any milestone'),
- });
- }
- if (showNo) {
- extraOptions.push({
- id: -1,
- name: __('No milestone'),
- title: __('No milestone'),
- });
- }
- if (showUpcoming) {
- extraOptions.push({
- id: -2,
- name: '#upcoming',
- title: __('Upcoming'),
- });
- }
- if (showStarted) {
- extraOptions.push({
- id: -3,
- name: '#started',
- title: __('Started'),
- });
- }
- if (extraOptions.length) {
- extraOptions.push({ type: 'divider' });
- }
+ data: (term, callback) => {
+ let contextId = $dropdown.get(0).dataset.projectId;
+ let getMilestones = Api.projectMilestones;
- callback(extraOptions.concat(data));
- if (showMenuAbove) {
- $dropdown.data('glDropdown').positionMenuAbove();
- }
- $(`[data-milestone-id="${escape(selectedMilestone)}"] > a`).addClass('is-active');
- }),
- renderRow: milestone => `
- <li data-milestone-id="${escape(milestone.name)}">
+ if (!contextId) {
+ contextId = $dropdown.get(0).dataset.groupId;
+ getMilestones = Api.groupMilestones;
+ }
+
+ // We don't use $.data() as it caches initial value and never updates!
+ return getMilestones(contextId, { state: 'active' })
+ .then(({ data }) =>
+ data
+ .map(m => ({
+ ...m,
+ // Public API includes `title` instead of `name`.
+ name: m.title,
+ }))
+ .sort((mA, mB) => {
+ // Move all expired milestones to the bottom.
+ if (mA.expired) {
+ return 1;
+ }
+ if (mB.expired) {
+ return -1;
+ }
+ return 0;
+ }),
+ )
+ .then(data => {
+ const extraOptions = [];
+ if (showAny) {
+ extraOptions.push({
+ id: null,
+ name: null,
+ title: __('Any milestone'),
+ });
+ }
+ if (showNo) {
+ extraOptions.push({
+ id: -1,
+ name: __('No milestone'),
+ title: __('No milestone'),
+ });
+ }
+ if (showUpcoming) {
+ extraOptions.push({
+ id: -2,
+ name: '#upcoming',
+ title: __('Upcoming'),
+ });
+ }
+ if (showStarted) {
+ extraOptions.push({
+ id: -3,
+ name: '#started',
+ title: __('Started'),
+ });
+ }
+ if (extraOptions.length) {
+ extraOptions.push({ type: 'divider' });
+ }
+
+ callback(extraOptions.concat(data));
+ if (showMenuAbove) {
+ $dropdown.data('glDropdown').positionMenuAbove();
+ }
+ $(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active');
+ });
+ },
+ renderRow: milestone => {
+ const milestoneName = milestone.title || milestone.name;
+ let milestoneDisplayName = escape(milestoneName);
+
+ if (milestone.expired) {
+ milestoneDisplayName = sprintf(__('%{milestone} (expired)'), {
+ milestone: milestoneDisplayName,
+ });
+ }
+
+ return `
+ <li data-milestone-id="${escape(milestoneName)}">
<a href='#' class='dropdown-menu-milestone-link'>
- ${escape(milestone.title)}
+ ${milestoneDisplayName}
</a>
</li>
- `,
+ `;
+ },
filterable: true,
search: {
fields: ['title'],
@@ -149,7 +193,7 @@ export default class MilestoneSelect {
selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault;
}
$('a.is-active', $el).removeClass('is-active');
- $(`[data-milestone-id="${escape(selectedMilestone)}"] > a`, $el).addClass('is-active');
+ $(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active');
},
vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: clickEvent => {
@@ -237,7 +281,16 @@ export default class MilestoneSelect {
if (data.milestone != null) {
data.milestone.remaining = timeFor(data.milestone.due_date);
data.milestone.name = data.milestone.title;
- $value.html(milestoneLinkTemplate(data.milestone));
+ $value.html(
+ data.milestone.expired
+ ? milestoneExpiredLinkTemplate({
+ ...data.milestone,
+ remaining: sprintf(__('%{due_date} (Past due)'), {
+ due_date: dateInWords(parsePikadayDate(data.milestone.due_date)),
+ }),
+ })
+ : milestoneLinkTemplate(data.milestone),
+ );
return $sidebarCollapsedValue
.attr(
'data-original-title',
diff --git a/app/assets/javascripts/static_site_editor/components/edit_area.vue b/app/assets/javascripts/static_site_editor/components/edit_area.vue
index b052c211542..84a16f327d9 100644
--- a/app/assets/javascripts/static_site_editor/components/edit_area.vue
+++ b/app/assets/javascripts/static_site_editor/components/edit_area.vue
@@ -5,6 +5,8 @@ import EditHeader from './edit_header.vue';
import UnsavedChangesConfirmDialog from './unsaved_changes_confirm_dialog.vue';
import parseSourceFile from '~/static_site_editor/services/parse_source_file';
import { EDITOR_TYPES } from '~/vue_shared/components/rich_content_editor/constants';
+import { DEFAULT_IMAGE_UPLOAD_PATH } from '../constants';
+import imageRepository from '../image_repository';
export default {
components: {
@@ -31,6 +33,12 @@ export default {
required: false,
default: '',
},
+ imageRoot: {
+ type: String,
+ required: false,
+ default: DEFAULT_IMAGE_UPLOAD_PATH,
+ validator: prop => prop.endsWith('/'),
+ },
},
data() {
return {
@@ -40,6 +48,7 @@ export default {
isModified: false,
};
},
+ imageRepository: imageRepository(),
computed: {
editableContent() {
return this.parsedSource.content(this.isWysiwygMode);
@@ -57,8 +66,14 @@ export default {
this.editorMode = mode;
this.$refs.editor.resetInitialValue(this.editableContent);
},
+ onUploadImage({ file, imageUrl }) {
+ this.$options.imageRepository.add(file, imageUrl);
+ },
onSubmit() {
- this.$emit('submit', { content: this.parsedSource.content() });
+ this.$emit('submit', {
+ content: this.parsedSource.content(),
+ images: this.$options.imageRepository.getAll(),
+ });
},
},
};
@@ -70,9 +85,11 @@ export default {
ref="editor"
:content="editableContent"
:initial-edit-type="editorMode"
+ :image-root="imageRoot"
class="mb-9 h-100"
@modeChange="onModeChange"
@input="onInputChange"
+ @uploadImage="onUploadImage"
/>
<unsaved-changes-confirm-dialog :modified="isModified" />
<publish-toolbar
diff --git a/app/assets/javascripts/static_site_editor/constants.js b/app/assets/javascripts/static_site_editor/constants.js
index 947347922f2..49db9ab7ca5 100644
--- a/app/assets/javascripts/static_site_editor/constants.js
+++ b/app/assets/javascripts/static_site_editor/constants.js
@@ -19,3 +19,5 @@ export const DEFAULT_HEADING = s__('StaticSiteEditor|Static site editor');
export const TRACKING_ACTION_CREATE_COMMIT = 'create_commit';
export const TRACKING_ACTION_CREATE_MERGE_REQUEST = 'create_merge_request';
export const TRACKING_ACTION_INITIALIZE_EDITOR = 'initialize_editor';
+
+export const DEFAULT_IMAGE_UPLOAD_PATH = 'source/images/uploads/';
diff --git a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js
index 6c4e3a4d973..0cb26f88785 100644
--- a/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js
+++ b/app/assets/javascripts/static_site_editor/graphql/resolvers/submit_content_changes.js
@@ -3,10 +3,10 @@ import savedContentMetaQuery from '../queries/saved_content_meta.query.graphql';
const submitContentChangesResolver = (
_,
- { input: { project: projectId, username, sourcePath, content } },
+ { input: { project: projectId, username, sourcePath, content, images } },
{ cache },
) => {
- return submitContentChanges({ projectId, username, sourcePath, content }).then(
+ return submitContentChanges({ projectId, username, sourcePath, content, images }).then(
savedContentMeta => {
cache.writeQuery({
query: savedContentMetaQuery,
diff --git a/app/assets/javascripts/static_site_editor/image_repository.js b/app/assets/javascripts/static_site_editor/image_repository.js
new file mode 100644
index 00000000000..541d581bda8
--- /dev/null
+++ b/app/assets/javascripts/static_site_editor/image_repository.js
@@ -0,0 +1,20 @@
+import { __ } from '~/locale';
+import Flash from '~/flash';
+import { getBinary } from './services/image_service';
+
+const imageRepository = () => {
+ const images = new Map();
+ const flash = message => new Flash(message);
+
+ const add = (file, url) => {
+ getBinary(file)
+ .then(content => images.set(url, content))
+ .catch(() => flash(__('Something went wrong while inserting your image. Please try again.')));
+ };
+
+ const getAll = () => images;
+
+ return { add, getAll };
+};
+
+export default imageRepository;
diff --git a/app/assets/javascripts/static_site_editor/pages/home.vue b/app/assets/javascripts/static_site_editor/pages/home.vue
index a1314c8a478..156b815e07a 100644
--- a/app/assets/javascripts/static_site_editor/pages/home.vue
+++ b/app/assets/javascripts/static_site_editor/pages/home.vue
@@ -67,11 +67,11 @@ export default {
onDismissError() {
this.submitChangesError = null;
},
- onSubmit({ content }) {
+ onSubmit({ content, images }) {
this.content = content;
- this.submitChanges();
+ this.submitChanges(images);
},
- submitChanges() {
+ submitChanges(images) {
this.isSavingChanges = true;
this.$apollo
@@ -83,6 +83,7 @@ export default {
username: this.appData.username,
sourcePath: this.appData.sourcePath,
content: this.content,
+ images,
},
},
})
diff --git a/app/assets/javascripts/static_site_editor/services/image_service.js b/app/assets/javascripts/static_site_editor/services/image_service.js
new file mode 100644
index 00000000000..edc69d0579a
--- /dev/null
+++ b/app/assets/javascripts/static_site_editor/services/image_service.js
@@ -0,0 +1,9 @@
+// eslint-disable-next-line import/prefer-default-export
+export const getBinary = file => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = () => resolve(reader.result.split(',')[1]);
+ reader.onerror = error => reject(error);
+ });
+};
diff --git a/app/assets/javascripts/static_site_editor/services/submit_content_changes.js b/app/assets/javascripts/static_site_editor/services/submit_content_changes.js
index fce7c1f918f..da62d3fa4fc 100644
--- a/app/assets/javascripts/static_site_editor/services/submit_content_changes.js
+++ b/app/assets/javascripts/static_site_editor/services/submit_content_changes.js
@@ -21,7 +21,32 @@ const createBranch = (projectId, branch) =>
throw new Error(SUBMIT_CHANGES_BRANCH_ERROR);
});
-const commitContent = (projectId, message, branch, sourcePath, content) => {
+const createImageActions = (images, markdown) => {
+ const actions = [];
+
+ if (!markdown) {
+ return actions;
+ }
+
+ images.forEach((imageContent, filePath) => {
+ const imageExistsInMarkdown = path => new RegExp(`!\\[([^[\\]\\n]*)\\](\\(${path})\\)`); // matches the image markdown syntax: ![<any-string-except-newline>](<path>)
+
+ if (imageExistsInMarkdown(filePath).test(markdown)) {
+ actions.push(
+ convertObjectPropsToSnakeCase({
+ encoding: 'base64',
+ action: 'create',
+ content: imageContent,
+ filePath,
+ }),
+ );
+ }
+ });
+
+ return actions;
+};
+
+const commitContent = (projectId, message, branch, sourcePath, content, images) => {
Tracking.event(document.body.dataset.page, TRACKING_ACTION_CREATE_COMMIT);
return Api.commitMultiple(
@@ -35,6 +60,7 @@ const commitContent = (projectId, message, branch, sourcePath, content) => {
filePath: sourcePath,
content,
}),
+ ...createImageActions(images, content),
],
}),
).catch(() => {
@@ -62,7 +88,7 @@ const createMergeRequest = (
});
};
-const submitContentChanges = ({ username, projectId, sourcePath, content }) => {
+const submitContentChanges = ({ username, projectId, sourcePath, content, images }) => {
const branch = generateBranchName(username);
const mergeRequestTitle = sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), {
sourcePath,
@@ -73,7 +99,7 @@ const submitContentChanges = ({ username, projectId, sourcePath, content }) => {
.then(({ data: { web_url: url } }) => {
Object.assign(meta, { branch: { label: branch, url } });
- return commitContent(projectId, mergeRequestTitle, branch, sourcePath, content);
+ return commitContent(projectId, mergeRequestTitle, branch, sourcePath, content, images);
})
.then(({ data: { short_id: label, web_url: url } }) => {
Object.assign(meta, { commit: { label, url } });
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue
index 6dac448d5de..13c42d35b04 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue
@@ -68,6 +68,7 @@ export default {
:is-applying-batch="suggestion.is_applying_batch"
:batch-suggestions-count="batchSuggestionsCount"
:help-page-path="helpPagePath"
+ :inapplicable-reason="suggestion.inapplicable_reason"
@apply="applySuggestion"
@applyBatch="applySuggestionBatch"
@addToBatch="addSuggestionToBatch"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue
index 54e837882ce..4de80e9b4c2 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue
@@ -38,6 +38,11 @@ export default {
type: String,
required: true,
},
+ inapplicableReason: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
data() {
return {
@@ -52,9 +57,7 @@ export default {
return this.isApplyingSingle || this.isApplyingBatch;
},
tooltipMessage() {
- return this.canApply
- ? __('This also resolves this thread')
- : __("Can't apply as this line has changed or the suggestion already matches its content.");
+ return this.canApply ? __('This also resolves this thread') : this.inapplicableReason;
},
isDisableButton() {
return this.isApplying || !this.canApply;
diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue b/app/assets/javascripts/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue
index dce5d1778b3..0a444b2295d 100644
--- a/app/assets/javascripts/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/modals/add_image/add_image_modal.vue
@@ -16,8 +16,15 @@ export default {
GlTab,
},
mixins: [glFeatureFlagMixin()],
+ props: {
+ imageRoot: {
+ type: String,
+ required: true,
+ },
+ },
data() {
return {
+ file: null,
urlError: null,
imageUrl: null,
description: null,
@@ -38,6 +45,7 @@ export default {
},
methods: {
show() {
+ this.file = null;
this.urlError = null;
this.imageUrl = null;
this.description = null;
@@ -66,7 +74,9 @@ export default {
return;
}
- this.$emit('addImage', { file, altText: altText || file.name });
+ const imageUrl = `${this.imageRoot}${file.name}`;
+
+ this.$emit('addImage', { imageUrl, file, altText: altText || file.name });
},
submitURL(event) {
if (!this.validateUrl()) {
diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue b/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue
index a32114b6a29..193310d1dc9 100644
--- a/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue
+++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/rich_content_editor.vue
@@ -19,8 +19,6 @@ import {
getMarkdown,
} from './services/editor_service';
-import { getUrl } from './services/image_service';
-
export default {
components: {
ToastEditor: () =>
@@ -54,6 +52,11 @@ export default {
required: false,
default: EDITOR_PREVIEW_STYLE,
},
+ imageRoot: {
+ type: String,
+ required: true,
+ validator: prop => prop.endsWith('/'),
+ },
},
data() {
return {
@@ -104,10 +107,8 @@ export default {
const image = { imageUrl, altText };
if (file) {
- image.imageUrl = getUrl(file);
- // TODO - persist images locally (local image repository)
+ this.$emit('uploadImage', { file, imageUrl });
// TODO - ensure that the actual repo URL for the image is used in Markdown mode
- // TODO - upload images to the project repository (on submit)
}
addImage(this.editorInstance, image);
@@ -130,6 +131,6 @@ export default {
@change="onContentChanged"
@load="onLoad"
/>
- <add-image-modal ref="addImageModal" @addImage="onAddImage" />
+ <add-image-modal ref="addImageModal" :image-root="imageRoot" @addImage="onAddImage" />
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/image_service.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/image_service.js
deleted file mode 100644
index a66e464e702..00000000000
--- a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/image_service.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// eslint-disable-next-line import/prefer-default-export
-export const getUrl = file => URL.createObjectURL(file);
diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss
index 4f66d6bf354..10796f319bf 100644
--- a/app/assets/stylesheets/framework/system_messages.scss
+++ b/app/assets/stylesheets/framework/system_messages.scss
@@ -94,7 +94,8 @@
margin-bottom: 16px;
}
- .boards-list {
+ .boards-list,
+ .board-swimlanes {
height: calc(100vh - #{$header-height + $breadcrumb-min-height + $performance-bar-height + $system-footer-height + $gl-padding-32});
}
}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index c1f5b3a3c7b..049660220df 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -45,7 +45,8 @@
}
}
-.boards-list {
+.boards-list,
+.board-swimlanes {
height: calc(100vh - #{$issue-board-list-difference-xs});
overflow-x: scroll;
min-height: 200px;
@@ -576,29 +577,8 @@
}
}
-.board-epics-swimlanes {
+.board-swimlanes {
overflow-x: auto;
- min-height: calc(100vh - #{$issue-board-list-difference-xs});
-
- @include media-breakpoint-only(sm) {
- min-height: calc(100vh - #{$issue-board-list-difference-sm});
- }
-
- @include media-breakpoint-up(md) {
- min-height: calc(100vh - #{$issue-board-list-difference-md});
- }
-
- .with-performance-bar & {
- min-height: calc(100vh - #{$issue-board-list-difference-xs} - #{$performance-bar-height});
-
- @include media-breakpoint-only(sm) {
- min-height: calc(100vh - #{$issue-board-list-difference-sm} - #{$performance-bar-height});
- }
-
- @include media-breakpoint-up(md) {
- min-height: calc(100vh - #{$issue-board-list-difference-md} - #{$performance-bar-height});
- }
- }
}
.board-header-collapsed-info-icon:hover {
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 94af1df2ccb..8daa622dc7c 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -108,12 +108,3 @@
.gl-transition-property-stroke {
transition-property: stroke;
}
-
-// temporary class till giltab-ui one is merged
-.gl-border-t-2 {
- border-top-width: $gl-border-size-2;
-}
-
-.gl-border-b-2 {
- border-bottom-width: $gl-border-size-2;
-}