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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-23 15:10:24 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-23 15:10:24 +0300
commite6d048d769240760008f0dbb6b811e1ebc675292 (patch)
treee005f05e66518044c7e8bc33e263f12f2561708d /app
parent611b009e924c25fcc8706b52af90542c4b78f0c0 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/diffs/components/app.vue28
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue6
-rw-r--r--app/assets/javascripts/diffs/components/tree_list.vue4
-rw-r--r--app/assets/javascripts/diffs/store/actions.js11
-rw-r--r--app/assets/javascripts/issues/constants.js1
-rw-r--r--app/assets/javascripts/milestones/index.js26
-rw-r--r--app/assets/javascripts/vue_shared/components/timezone_dropdown/timezone_dropdown.vue67
-rw-r--r--app/controllers/groups/milestones_controller.rb14
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/controllers/projects/milestones_controller.rb11
-rw-r--r--app/finders/crm/organizations_finder.rb38
-rw-r--r--app/helpers/safe_format_helper.rb13
-rw-r--r--app/mailers/emails/service_desk.rb5
-rw-r--r--app/models/group.rb2
-rw-r--r--app/services/groups/transfer_service.rb2
-rw-r--r--app/views/projects/merge_requests/_page.html.haml2
-rw-r--r--app/views/shared/milestones/_description.html.haml4
17 files changed, 124 insertions, 111 deletions
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 02307150e2f..f644c69f0a0 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -325,7 +325,7 @@ export default {
this.adjustView();
},
viewDiffsFileByFile(newViewFileByFile) {
- if (!newViewFileByFile && this.diffsIncomplete && this.glFeatures.singleFileFileByFile) {
+ if (!newViewFileByFile && this.diffsIncomplete) {
this.refetchDiffData({ refetchMeta: false });
}
},
@@ -467,26 +467,19 @@ export default {
subscribeToEvents() {
notesEventHub.$once('fetchDiffData', this.fetchData);
notesEventHub.$on('refetchDiffData', this.refetchDiffData);
- if (this.glFeatures.singleFileFileByFile) {
- diffsEventHub.$on('diffFilesModified', this.setDiscussions);
- notesEventHub.$on('fetchedNotesData', this.rereadNoteHash);
- }
+ notesEventHub.$on('fetchedNotesData', this.rereadNoteHash);
+ diffsEventHub.$on('diffFilesModified', this.setDiscussions);
diffsEventHub.$on(EVT_MR_PREPARED, this.fetchData);
},
unsubscribeFromEvents() {
diffsEventHub.$off(EVT_MR_PREPARED, this.fetchData);
- if (this.glFeatures.singleFileFileByFile) {
- notesEventHub.$off('fetchedNotesData', this.rereadNoteHash);
- diffsEventHub.$off('diffFilesModified', this.setDiscussions);
- }
+ diffsEventHub.$off('diffFilesModified', this.setDiscussions);
+ notesEventHub.$off('fetchedNotesData', this.rereadNoteHash);
notesEventHub.$off('refetchDiffData', this.refetchDiffData);
notesEventHub.$off('fetchDiffData', this.fetchData);
},
navigateToDiffFileNumber(number) {
- this.navigateToDiffFileIndex({
- index: number - 1,
- singleFile: this.glFeatures.singleFileFileByFile,
- });
+ this.navigateToDiffFileIndex(number - 1);
},
refetchDiffData({ refetchMeta = true } = {}) {
this.fetchData({ toggleTree: false, fetchMeta: refetchMeta });
@@ -506,7 +499,7 @@ export default {
if (data) {
realSize = data.real_size;
- if (this.viewDiffsFileByFile && this.glFeatures.singleFileFileByFile) {
+ if (this.viewDiffsFileByFile) {
this.fetchFileByFile();
}
}
@@ -527,7 +520,7 @@ export default {
});
}
- if (!this.viewDiffsFileByFile || !this.glFeatures.singleFileFileByFile) {
+ if (!this.viewDiffsFileByFile) {
this.fetchDiffFilesBatch()
.then(() => {
if (toggleTree) this.setTreeDisplay();
@@ -618,10 +611,7 @@ export default {
jumpToFile(step) {
const targetIndex = this.currentDiffIndex + step;
if (targetIndex >= 0 && targetIndex < this.flatBlobsList.length) {
- this.goToFile({
- path: this.flatBlobsList[targetIndex].path,
- singleFile: this.glFeatures.singleFileFileByFile,
- });
+ this.goToFile({ path: this.flatBlobsList[targetIndex].path });
}
},
setTreeDisplay() {
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 4c2cb83ffb3..64a7c047cb4 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -209,12 +209,6 @@ export default {
if (this.hasDiff) {
this.postRender();
- } else if (
- this.viewDiffsFileByFile &&
- !this.isCollapsed &&
- !this.glFeatures.singleFileFileByFile
- ) {
- this.requestDiff();
}
this.manageViewedEffects();
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue
index 4f1875e9175..544bbbfe9d8 100644
--- a/app/assets/javascripts/diffs/components/tree_list.vue
+++ b/app/assets/javascripts/diffs/components/tree_list.vue
@@ -5,7 +5,6 @@ import micromatch from 'micromatch';
import { debounce } from 'lodash';
import { getModifierKey } from '~/constants';
import { s__, sprintf } from '~/locale';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { RecycleScroller } from 'vendor/vue-virtual-scroller';
import DiffFileRow from './diff_file_row.vue';
@@ -20,7 +19,6 @@ export default {
DiffFileRow,
RecycleScroller,
},
- mixins: [glFeatureFlagsMixin()],
props: {
hideFileStats: {
type: Boolean,
@@ -177,7 +175,7 @@ export default {
:class="{ 'tree-list-parent': item.level > 0 }"
class="gl-relative"
@toggleTreeOpen="toggleTreeOpen"
- @clickFile="(path) => goToFile({ singleFile: glFeatures.singleFileFileByFile, path })"
+ @clickFile="(path) => goToFile({ path })"
/>
</template>
<template #after>
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 3be6562f586..30c34e9a5be 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -608,8 +608,8 @@ export const setCurrentFileHash = ({ commit }, hash) => {
commit(types.SET_CURRENT_DIFF_FILE, hash);
};
-export const goToFile = ({ state, commit, dispatch, getters }, { path, singleFile }) => {
- if (!state.viewDiffsFileByFile || !singleFile) {
+export const goToFile = ({ state, commit, dispatch, getters }, { path }) => {
+ if (!state.viewDiffsFileByFile) {
dispatch('scrollToFile', { path });
} else {
if (!state.treeEntries[path]) return;
@@ -943,16 +943,13 @@ export const setCurrentDiffFileIdFromNote = ({ commit, getters, rootGetters }, n
}
};
-export const navigateToDiffFileIndex = (
- { state, getters, commit, dispatch },
- { index, singleFile },
-) => {
+export const navigateToDiffFileIndex = ({ state, getters, commit, dispatch }, index) => {
const { fileHash } = getters.flatBlobsList[index];
document.location.hash = fileHash;
commit(types.SET_CURRENT_DIFF_FILE, fileHash);
- if (state.viewDiffsFileByFile && singleFile) {
+ if (state.viewDiffsFileByFile) {
dispatch('fetchFileByFile');
}
};
diff --git a/app/assets/javascripts/issues/constants.js b/app/assets/javascripts/issues/constants.js
index c79612ad5d0..444ee704521 100644
--- a/app/assets/javascripts/issues/constants.js
+++ b/app/assets/javascripts/issues/constants.js
@@ -14,6 +14,7 @@ export const TYPE_EPIC = 'epic';
export const TYPE_INCIDENT = 'incident';
export const TYPE_ISSUE = 'issue';
export const TYPE_MERGE_REQUEST = 'merge_request';
+export const TYPE_MILESTONE = 'milestone';
export const TYPE_TEST_CASE = 'test_case';
export const WORKSPACE_GROUP = 'group';
diff --git a/app/assets/javascripts/milestones/index.js b/app/assets/javascripts/milestones/index.js
index 8780d931588..420f7cee4d2 100644
--- a/app/assets/javascripts/milestones/index.js
+++ b/app/assets/javascripts/milestones/index.js
@@ -9,12 +9,18 @@ import Sidebar from '~/right_sidebar';
import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar';
import Translate from '~/vue_shared/translate';
import ZenMode from '~/zen_mode';
+import TaskList from '~/task_list';
+import { TYPE_MILESTONE } from '~/issues/constants';
+import { createAlert } from '~/alert';
+import { __ } from '~/locale';
import DeleteMilestoneModal from './components/delete_milestone_modal.vue';
import PromoteMilestoneModal from './components/promote_milestone_modal.vue';
import eventHub from './event_hub';
// See app/views/shared/milestones/_description.html.haml
export const MILESTONE_DESCRIPTION_ELEMENT = '.milestone-detail .description';
+export const MILESTONE_DESCRIPTION_TASK_LIST_CONTAINER_ELEMENT = `${MILESTONE_DESCRIPTION_ELEMENT}.js-task-list-container`;
+export const MILESTONE_DETAIL_ELEMENT = '.milestone-detail';
export function initForm(initGFM = true) {
new ZenMode(); // eslint-disable-line no-new
@@ -40,6 +46,26 @@ export function initShow() {
new MountMilestoneSidebar(); // eslint-disable-line no-new
renderGFM(document.querySelector(MILESTONE_DESCRIPTION_ELEMENT));
+
+ const el = document.querySelector(MILESTONE_DESCRIPTION_TASK_LIST_CONTAINER_ELEMENT);
+
+ if (!el) {
+ return null;
+ }
+
+ return new TaskList({
+ dataType: TYPE_MILESTONE,
+ fieldName: 'description',
+ selector: MILESTONE_DETAIL_ELEMENT,
+ lockVersion: el.dataset.lockVersion,
+ onError: () => {
+ createAlert({
+ message: __(
+ 'Someone edited this milestone at the same time you did. Please refresh the page to see changes.',
+ ),
+ });
+ },
+ });
}
export function initPromoteMilestoneModal() {
diff --git a/app/assets/javascripts/vue_shared/components/timezone_dropdown/timezone_dropdown.vue b/app/assets/javascripts/vue_shared/components/timezone_dropdown/timezone_dropdown.vue
index 247f49c1345..04dc3b20dff 100644
--- a/app/assets/javascripts/vue_shared/components/timezone_dropdown/timezone_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/timezone_dropdown/timezone_dropdown.vue
@@ -1,18 +1,12 @@
<script>
-import { GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { __ } from '~/locale';
-import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
import { formatTimezone } from '~/lib/utils/datetime_utility';
export default {
name: 'TimezoneDropdown',
components: {
- GlDropdown,
- GlDropdownItem,
- GlSearchBoxByType,
- },
- directives: {
- autofocusonshow,
+ GlCollapsibleListbox,
},
props: {
value: {
@@ -52,11 +46,10 @@ export default {
identifier: timezone.identifier,
}));
},
- filteredResults() {
- const lowerCasedSearchTerm = this.searchTerm.toLowerCase();
- return this.timezones.filter((timezone) =>
- timezone.formattedTimezone.toLowerCase().includes(lowerCasedSearchTerm),
- );
+ filteredListboxItems() {
+ return this.timezones
+ .filter((timezone) => timezone.formattedTimezone.toLowerCase().includes(this.searchTerm))
+ .map(({ formattedTimezone }) => ({ value: formattedTimezone, text: formattedTimezone }));
},
selectedTimezoneLabel() {
return this.tzValue || __('Select timezone');
@@ -68,14 +61,14 @@ export default {
},
},
methods: {
- selectTimezone(selectedTimezone) {
- this.tzValue = selectedTimezone.formattedTimezone;
+ selectTimezone(formattedTimezone) {
+ const selectedTimezone = this.timezones.find(
+ (timezone) => timezone.formattedTimezone === formattedTimezone,
+ );
+ this.tzValue = formattedTimezone;
this.$emit('input', selectedTimezone);
this.searchTerm = '';
},
- isSelected(timezone) {
- return this.tzValue === timezone.formattedTimezone;
- },
initialTimezone(timezones, value) {
if (!value) {
return undefined;
@@ -89,6 +82,9 @@ export default {
return undefined;
},
+ setSearchTerm(value) {
+ this.searchTerm = value?.toLowerCase();
+ },
},
};
</script>
@@ -101,31 +97,16 @@ export default {
:value="timezoneIdentifier || value"
type="hidden"
/>
- <gl-dropdown
- :text="selectedTimezoneLabel"
- :class="additionalClass"
+ <gl-collapsible-listbox
+ :items="filteredListboxItems"
+ :toggle-text="selectedTimezoneLabel"
+ :toggle-class="additionalClass"
+ :no-results-text="$options.translations.noResultsText"
+ :selected="tzValue"
block
- lazy
- menu-class="gl-w-full!"
- v-bind="$attrs"
- >
- <gl-search-box-by-type v-model.trim="searchTerm" v-autofocusonshow autofocus />
- <gl-dropdown-item
- v-for="timezone in filteredResults"
- :key="timezone.formattedTimezone"
- :is-checked="isSelected(timezone)"
- is-check-item
- @click="selectTimezone(timezone)"
- >
- {{ timezone.formattedTimezone }}
- </gl-dropdown-item>
- <gl-dropdown-item
- v-if="!filteredResults.length"
- class="gl-pointer-events-none"
- data-testid="noMatchingResults"
- >
- {{ $options.translations.noResultsText }}
- </gl-dropdown-item>
- </gl-dropdown>
+ searchable
+ @search="setSearchTerm"
+ @select="selectTimezone"
+ />
</div>
</template>
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 903c8c214ae..5f6b55ea928 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -44,7 +44,19 @@ class Groups::MilestonesController < Groups::ApplicationController
def update
Milestones::UpdateService.new(@milestone.parent, current_user, milestone_params).execute(@milestone)
- redirect_to milestone_path(@milestone)
+ respond_to do |format|
+ format.html do
+ redirect_to milestone_path(@milestone)
+ end
+
+ format.json do
+ if @milestone.valid?
+ head :no_content
+ else
+ render json: { errors: @milestone.errors.full_messages }, status: :unprocessable_entity
+ end
+ end
+ end
rescue ActiveRecord::StaleObjectError
respond_to do |format|
format.html do
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 91788444f1f..4423929e48b 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -44,7 +44,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:refactor_security_extension, @project)
push_frontend_feature_flag(:deprecate_vulnerabilities_feedback, @project)
push_frontend_feature_flag(:moved_mr_sidebar, project)
- push_frontend_feature_flag(:single_file_file_by_file, project)
push_frontend_feature_flag(:mr_experience_survey, project)
push_frontend_feature_flag(:realtime_mr_status_change, project)
push_frontend_feature_flag(:realtime_approvals, project)
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 569a514b23b..35b65dbce7e 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -76,7 +76,6 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestone = Milestones::UpdateService.new(project, current_user, milestone_params).execute(milestone)
respond_to do |format|
- format.js
format.html do
if @milestone.valid?
redirect_to project_milestone_path(@project, @milestone)
@@ -84,6 +83,16 @@ class Projects::MilestonesController < Projects::ApplicationController
render :edit
end
end
+
+ format.js
+
+ format.json do
+ if @milestone.valid?
+ head :no_content
+ else
+ render json: { errors: @milestone.errors.full_messages }, status: :unprocessable_entity
+ end
+ end
end
rescue ActiveRecord::StaleObjectError
respond_to do |format|
diff --git a/app/finders/crm/organizations_finder.rb b/app/finders/crm/organizations_finder.rb
index 69f72235c71..8701d26dd6e 100644
--- a/app/finders/crm/organizations_finder.rb
+++ b/app/finders/crm/organizations_finder.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-# Finder for retrieving organizations scoped to a group
+# Finder for retrieving crm_organizations scoped to a group
#
# Arguments:
# current_user - user performing the action. Must have the correct permission level for the group.
@@ -29,22 +29,22 @@ module Crm
def execute
return CustomerRelations::Organization.none unless root_group
- organizations = root_group.organizations
- organizations = by_ids(organizations)
- organizations = by_search(organizations)
- organizations = by_state(organizations)
- sort_organizations(organizations)
+ crm_organizations = root_group.crm_organizations
+ crm_organizations = by_ids(crm_organizations)
+ crm_organizations = by_search(crm_organizations)
+ crm_organizations = by_state(crm_organizations)
+ sort_crm_organizations(crm_organizations)
end
private
- def sort_organizations(organizations)
- return organizations.sort_by_name unless @params.key?(:sort)
- return organizations if @params[:sort].nil?
+ def sort_crm_organizations(crm_organizations)
+ return crm_organizations.sort_by_name unless @params.key?(:sort)
+ return crm_organizations if @params[:sort].nil?
field = @params[:sort][:field]
direction = @params[:sort][:direction]
- organizations.sort_by_field(field, direction)
+ crm_organizations.sort_by_field(field, direction)
end
def root_group
@@ -57,22 +57,22 @@ module Crm
end
end
- def by_search(organizations)
- return organizations unless search?
+ def by_search(crm_organizations)
+ return crm_organizations unless search?
- organizations.search(params[:search])
+ crm_organizations.search(params[:search])
end
- def by_state(organizations)
- return organizations unless state?
+ def by_state(crm_organizations)
+ return crm_organizations unless state?
- organizations.search_by_state(params[:state])
+ crm_organizations.search_by_state(params[:state])
end
- def by_ids(organizations)
- return organizations unless ids?
+ def by_ids(crm_organizations)
+ return crm_organizations unless ids?
- organizations.id_in(params[:ids])
+ crm_organizations.id_in(params[:ids])
end
def search?
diff --git a/app/helpers/safe_format_helper.rb b/app/helpers/safe_format_helper.rb
index f05cf5ab50f..96124e98d4e 100644
--- a/app/helpers/safe_format_helper.rb
+++ b/app/helpers/safe_format_helper.rb
@@ -1,23 +1,22 @@
# frozen_string_literal: true
module SafeFormatHelper
- # Returns a HTML-safe string where +format+ and +args+ are escaped via
- # `html_escape` if they are not marked as HTML-safe.
- #
- # Argument +format+ must not be marked as HTML-safe via `.html_safe`.
+ # Returns a HTML-safe string where
+ # * +format+ is escaped via `html_escape_once`
+ # * +args+ are escaped via `html_escape` if they are not marked as HTML-safe
#
# Example:
# safe_format('Some %{open}bold%{close} text.', open: '<strong>'.html_safe, close: '</strong>'.html_safe)
# # => 'Some <strong>bold</strong>'
# safe_format('See %{user_input}', user_input: '<b>bold</b>')
# # => 'See &lt;b&gt;bold&lt;/b&gt;
+ # safe_format('In &lt; hour & more')
+ # # => 'In &lt; hour &amp; more'
#
def safe_format(format, **args)
- raise ArgumentError, 'Argument `format` must not be marked as html_safe!' if format.html_safe?
-
# Use `Kernel.format` to avoid conflicts with ViewComponent's `format`.
Kernel.format(
- html_escape(format),
+ html_escape_once(format),
args.transform_values { |value| html_escape(value) }
).html_safe
end
diff --git a/app/mailers/emails/service_desk.rb b/app/mailers/emails/service_desk.rb
index c627f4633e4..a51a2a68da0 100644
--- a/app/mailers/emails/service_desk.rb
+++ b/app/mailers/emails/service_desk.rb
@@ -176,6 +176,11 @@ module Emails
.gsub(/%\{\s*SYSTEM_FOOTER\s*\}/, text_footer_message.to_s)
.gsub(/%\{\s*UNSUBSCRIBE_URL\s*\}/, unsubscribe_sent_notification_url(@sent_notification))
.gsub(/%\{\s*ADDITIONAL_TEXT\s*\}/, service_desk_email_additional_text.to_s)
+ .gsub(/%\{\s*ISSUE_URL\s*\}/, full_issue_url)
+ end
+
+ def full_issue_url
+ issue_url(@issue)
end
def issue_id
diff --git a/app/models/group.rb b/app/models/group.rb
index 844b7496913..9ef1d3f12f3 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -92,7 +92,7 @@ class Group < Namespace
has_many :badges, class_name: 'GroupBadge'
# AR defaults to nullify when trying to delete via has_many associations unless we set dependent: :delete_all
- has_many :organizations, class_name: 'CustomerRelations::Organization', inverse_of: :group, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
+ has_many :crm_organizations, class_name: 'CustomerRelations::Organization', inverse_of: :group, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :contacts, class_name: 'CustomerRelations::Contact', inverse_of: :group, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :cluster_groups, class_name: 'Clusters::Group'
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index 1c8df157716..16454360ee2 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -69,7 +69,7 @@ module Groups
return false if group.root_ancestor == @new_parent_group.root_ancestor
return true if group.contacts.exists? && !current_user.can?(:admin_crm_contact, @new_parent_group.root_ancestor)
- return true if group.organizations.exists? && !current_user.can?(:admin_crm_organization, @new_parent_group.root_ancestor)
+ return true if group.crm_organizations.exists? && !current_user.can?(:admin_crm_organization, @new_parent_group.root_ancestor)
false
end
diff --git a/app/views/projects/merge_requests/_page.html.haml b/app/views/projects/merge_requests/_page.html.haml
index 3e56148f777..5ea67376a86 100644
--- a/app/views/projects/merge_requests/_page.html.haml
+++ b/app/views/projects/merge_requests/_page.html.haml
@@ -16,7 +16,7 @@
- add_page_specific_style 'page_bundles/ci_status'
- add_page_startup_api_call @endpoint_metadata_url
-- if mr_action == 'diffs' && (!@file_by_file_default || !single_file_file_by_file?)
+- if mr_action == 'diffs' && !@file_by_file_default
- add_page_startup_api_call @endpoint_diff_batch_url
.merge-request{ data: { mr_action: mr_action, url: merge_request_path(@merge_request, format: :json), project_path: project_path(@merge_request.project), lock_version: @merge_request.lock_version, diffs_batch_cache_key: @diffs_batch_cache_key } }
diff --git a/app/views/shared/milestones/_description.html.haml b/app/views/shared/milestones/_description.html.haml
index a63702661d0..3774fb0869f 100644
--- a/app/views/shared/milestones/_description.html.haml
+++ b/app/views/shared/milestones/_description.html.haml
@@ -9,5 +9,7 @@
- if milestone.try(:description).present?
%div{ data: { qa_selector: "milestone_description_content" } }
- .description.md.gl-px-0.gl-pt-4
+ .description.md.gl-px-0.gl-pt-4{ class: ('js-task-list-container' if can?(current_user, :admin_milestone, milestone)), data: { lock_version: @milestone.lock_version } }
= markdown_field(milestone, :description)
+ -# This textarea is necessary for `task_list.js` to work.
+ %textarea.hidden.js-task-list-field{ data: { value: milestone.description, update_url: milestone_path(milestone, format: :json)} }