From 3db4e2960aebe87b0c5f67bbf9ad9c87c9b9d483 Mon Sep 17 00:00:00 2001
From: Janis Altherr <jaltherr@gitlab.com>
Date: Wed, 12 Jul 2023 04:11:19 +0000
Subject: [PATCH] Use Markdown editor in Milestone descriptions

This MR enables the new markdown editor wrapper
in Milestone descriptions as part of
https://gitlab.com/groups/gitlab-org/-/epics/7098

Changelog: changed
---
 app/assets/javascripts/milestones/index.js    | 19 ++------
 .../markdown/mount_markdown_editor.js         |  3 +-
 .../groups/milestones_controller.rb           |  4 ++
 .../projects/milestones_controller.rb         |  4 ++
 app/views/groups/milestones/_form.html.haml   | 16 ++++---
 app/views/projects/milestones/_form.html.haml | 17 +++++---
 spec/features/groups/milestone_spec.rb        |  2 +-
 spec/frontend/dropzone_input_spec.js          |  2 +-
 spec/frontend/fixtures/milestones.rb          | 43 -------------------
 spec/frontend/fixtures/static/textarea.html   | 27 ++++++++++++
 .../milestone_editing_shared_examples.rb      |  4 +-
 11 files changed, 65 insertions(+), 76 deletions(-)
 delete mode 100644 spec/frontend/fixtures/milestones.rb
 create mode 100644 spec/frontend/fixtures/static/textarea.html

diff --git a/app/assets/javascripts/milestones/index.js b/app/assets/javascripts/milestones/index.js
index 420f7cee4d2b8..403db0865f0fe 100644
--- a/app/assets/javascripts/milestones/index.js
+++ b/app/assets/javascripts/milestones/index.js
@@ -1,10 +1,9 @@
-import $ from 'jquery';
 import Vue from 'vue';
 import initDatePicker from '~/behaviors/date_picker';
-import GLForm from '~/gl_form';
 import { BV_SHOW_MODAL } from '~/lib/utils/constants';
 import Milestone from '~/milestones/milestone';
 import { renderGFM } from '~/behaviors/markdown/render_gfm';
+import { mountMarkdownEditor } from '~/vue_shared/components/markdown/mount_markdown_editor';
 import Sidebar from '~/right_sidebar';
 import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar';
 import Translate from '~/vue_shared/translate';
@@ -22,22 +21,10 @@ 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) {
+export function initForm() {
+  mountMarkdownEditor();
   new ZenMode(); // eslint-disable-line no-new
   initDatePicker();
-
-  // eslint-disable-next-line no-new
-  new GLForm($('.milestone-form'), {
-    emojis: true,
-    members: initGFM,
-    issues: initGFM,
-    mergeRequests: initGFM,
-    epics: initGFM,
-    milestones: initGFM,
-    labels: initGFM,
-    snippets: initGFM,
-    vulnerabilities: initGFM,
-  });
 }
 
 export function initShow() {
diff --git a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js
index e12815f0094ce..b7b154bfc236b 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js
+++ b/app/assets/javascripts/vue_shared/components/markdown/mount_markdown_editor.js
@@ -79,6 +79,7 @@ export function mountMarkdownEditor(options = {}) {
   const supportsQuickActions = parseBoolean(el.dataset.supportsQuickActions ?? true);
   const enableAutocomplete = parseBoolean(el.dataset.enableAutocomplete ?? true);
   const disableAttachments = parseBoolean(el.dataset.disableAttachments ?? false);
+  const autofocus = parseBoolean(el.dataset.autofocus ?? true);
   const hiddenInput = el.querySelector('input[type="hidden"]');
   const formFieldName = hiddenInput.getAttribute('name');
   const formFieldId = hiddenInput.getAttribute('id');
@@ -128,7 +129,7 @@ export function mountMarkdownEditor(options = {}) {
           autocompleteDataSources: gl.GfmAutoComplete?.dataSources,
           supportsQuickActions,
           disableAttachments,
-          autofocus: true,
+          autofocus,
         },
       });
     },
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 5f6b55ea9288f..cbed75019f2cd 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -9,6 +9,10 @@ class Groups::MilestonesController < Groups::ApplicationController
   feature_category :team_planning
   urgency :low
 
+  before_action do
+    push_frontend_feature_flag(:content_editor_on_issues, group)
+  end
+
   def index
     respond_to do |format|
       format.html do
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 35b65dbce7e0d..1f4e5b545004a 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -24,6 +24,10 @@ class Projects::MilestonesController < Projects::ApplicationController
   feature_category :team_planning
   urgency :low
 
+  before_action do
+    push_frontend_feature_flag(:content_editor_on_issues, @project)
+  end
+
   def index
     @sort = params[:sort] || 'due_date_asc'
     @milestones = milestones.sort_by_attribute(@sort)
diff --git a/app/views/groups/milestones/_form.html.haml b/app/views/groups/milestones/_form.html.haml
index 89f460606cbd4..e84fd7a869288 100644
--- a/app/views/groups/milestones/_form.html.haml
+++ b/app/views/groups/milestones/_form.html.haml
@@ -10,12 +10,16 @@
   = render "shared/milestones/form_dates", f: f
   .form-group
     = f.label :description, _("Description")
-    = render layout: 'shared/md_preview', locals: { url: group_preview_markdown_path } do
-      = render 'shared/zen', f: f, attr: :description,
-                              classes: 'note-textarea',
-                              qa_selector: 'milestone_description_field',
-                              supports_autocomplete: true,
-                              placeholder: _('Write milestone description...')
+    - @gfm_form = true
+    .js-markdown-editor{ data: { render_markdown_path: group_preview_markdown_path,
+                               markdown_docs_path: help_page_path('user/markdown'),
+                               qa_selector: 'milestone_description_field',
+                               form_field_placeholder: _('Write milestone description...'),
+                               supports_quick_actions: 'false',
+                               enable_autocomplete: 'true',
+                               autofocus: 'false',
+                               form_field_classes: 'note-textarea js-gfm-input markdown-area' } }
+      = f.hidden_field :description
       .clearfix
       .error-alert
 
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index be6f9ac83dc5c..a1300ccd83534 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -12,13 +12,16 @@
   = render 'shared/milestones/form_dates', f: f
   .form-group
     = f.label :description, _('Description')
-    = render layout: 'shared/md_preview', locals: { url: preview_markdown_path(@project) } do
-      = render 'shared/zen', f: f, attr: :description,
-                              classes: 'note-textarea',
-                              qa_selector: 'milestone_description_field',
-                              supports_autocomplete: true,
-                              placeholder: _('Write milestone description...')
-      = render 'shared/notes/hints'
+    - @gfm_form = true
+    .js-markdown-editor{ data: { render_markdown_path: preview_markdown_path(@project),
+                               markdown_docs_path: help_page_path('user/markdown'),
+                               qa_selector: 'milestone_description_field',
+                               form_field_placeholder: _('Write milestone description...'),
+                               supports_quick_actions: 'false',
+                               enable_autocomplete: 'true',
+                               autofocus: 'false',
+                               form_field_classes: 'note-textarea js-gfm-input markdown-area' } }
+      = f.hidden_field :description
     .clearfix
     .error-alert
 
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 0a697eaa798cd..bb7cc3db452c6 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -27,7 +27,7 @@
 
       click_button("Preview")
 
-      preview = find('.js-md-preview')
+      preview = find('.js-vue-md-preview')
 
       expect(preview).to have_content('Nothing to preview.')
 
diff --git a/spec/frontend/dropzone_input_spec.js b/spec/frontend/dropzone_input_spec.js
index 57debf79c7b95..ba4d838e44b7c 100644
--- a/spec/frontend/dropzone_input_spec.js
+++ b/spec/frontend/dropzone_input_spec.js
@@ -1,6 +1,5 @@
 import MockAdapter from 'axios-mock-adapter';
 import $ from 'jquery';
-import htmlNewMilestone from 'test_fixtures/milestones/new-milestone.html';
 import mock from 'xhr-mock';
 import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
 import waitForPromises from 'helpers/wait_for_promises';
@@ -9,6 +8,7 @@ import PasteMarkdownTable from '~/behaviors/markdown/paste_markdown_table';
 import dropzoneInput from '~/dropzone_input';
 import axios from '~/lib/utils/axios_utils';
 import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
+import htmlNewMilestone from 'test_fixtures_static/textarea.html';
 
 const TEST_FILE = new File([], 'somefile.jpg');
 TEST_FILE.upload = {};
diff --git a/spec/frontend/fixtures/milestones.rb b/spec/frontend/fixtures/milestones.rb
deleted file mode 100644
index 5e39dcf190a6b..0000000000000
--- a/spec/frontend/fixtures/milestones.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Projects::MilestonesController, '(JavaScript fixtures)', :with_license, feature_category: :team_planning, type: :controller do
-  include JavaScriptFixturesHelpers
-
-  let_it_be(:user) { create(:user, feed_token: 'feedtoken:coldfeed') }
-  let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures') }
-  let_it_be(:project) { create(:project_empty_repo, namespace: namespace, path: 'milestones-project') }
-
-  render_views
-
-  before do
-    project.add_maintainer(user)
-    sign_in(user)
-  end
-
-  after do
-    remove_repository(project)
-  end
-
-  it 'milestones/new-milestone.html' do
-    get :new, params: {
-      namespace_id: project.namespace.to_param,
-      project_id: project
-    }
-
-    expect(response).to be_successful
-  end
-
-  private
-
-  def render_milestone(milestone)
-    get :show, params: {
-      namespace_id: project.namespace.to_param,
-      project_id: project,
-      id: milestone.to_param
-    }
-
-    expect(response).to be_successful
-  end
-end
diff --git a/spec/frontend/fixtures/static/textarea.html b/spec/frontend/fixtures/static/textarea.html
new file mode 100644
index 0000000000000..68d5a0f2d4d2c
--- /dev/null
+++ b/spec/frontend/fixtures/static/textarea.html
@@ -0,0 +1,27 @@
+<body>
+<meta charset="utf-8">
+<title>Document with Textarea</title>
+<form class="milestone-form common-note-form js-quick-submit js-requires-input" id="new_milestone"
+      action="http://test.host/frontend-fixtures/milestones-project/-/milestones"
+      accept-charset="UTF-8" method="post">
+    <div class="form-group">
+        <div class="md-write-holder">
+            <div class="zen-backdrop">
+                <textarea class="note-textarea js-gfm-input js-autosize markdown-area"
+                          placeholder="Write milestone description..." dir="auto"
+                          data-supports-quick-actions="false" data-supports-autocomplete="true"
+                          data-qa-selector="milestone_description_field" data-autofocus="false"
+                          name="milestone[description]"
+                          id="milestone_description"></textarea>
+                <a class="zen-control zen-control-leave js-zen-leave gl-text-gray-500"
+                   href="#">
+                    <svg class="s16" data-testid="minimize-icon">
+                        <use href="http://test.host/assets/icons-b8c5a9711f73b1de3c81754da0aca72f43b0e6844aa06dd03092b601a493f45b.svg#minimize"></use>
+                    </svg>
+                </a>
+            </div>
+        </div>
+    </div>
+</form>
+
+</body>
diff --git a/spec/support/shared_examples/features/milestone_editing_shared_examples.rb b/spec/support/shared_examples/features/milestone_editing_shared_examples.rb
index d21bf62ecfa5e..53498a1bb3963 100644
--- a/spec/support/shared_examples/features/milestone_editing_shared_examples.rb
+++ b/spec/support/shared_examples/features/milestone_editing_shared_examples.rb
@@ -1,7 +1,9 @@
 # frozen_string_literal: true
 
 RSpec.shared_examples 'milestone handling version conflicts' do
-  it 'warns about version conflict when milestone has been updated in the background' do
+  it 'warns about version conflict when milestone has been updated in the background', :js do
+    wait_for_all_requests
+
     # Update the milestone in the background in order to trigger a version conflict
     milestone.update!(title: "New title")
 
-- 
GitLab