From 41e78ef065a76627743876cf982bcfd68bfc8759 Mon Sep 17 00:00:00 2001
From: Chad Lavimoniere <clavimoniere@gitlab.com>
Date: Fri, 1 Dec 2023 10:51:05 +0000
Subject: [PATCH] Adjust styling of md editor header

- Add dividers between groups of related buttons
- Ensure fullscreen button is in top left corner of header toolbar

Changelog: fixed
---
 .../markdown/comment_templates_dropdown.vue   |   2 +-
 .../vue_shared/components/markdown/header.vue | 498 +++++++++---------
 .../components/markdown/header_divider.vue    |  16 +
 .../components/markdown/toolbar_button.vue    |   2 +-
 .../ai/components/ai_actions_dropdown.vue     |   2 +-
 5 files changed, 280 insertions(+), 240 deletions(-)
 create mode 100644 app/assets/javascripts/vue_shared/components/markdown/header_divider.vue

diff --git a/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue b/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue
index d99b90fa5618c..a7dfc1e2cdb1a 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/comment_templates_dropdown.vue
@@ -88,7 +88,7 @@ export default {
       placement="right"
       searchable
       size="small"
-      class="comment-template-dropdown gl-mr-3"
+      class="comment-template-dropdown gl-mr-2"
       positioning-strategy="fixed"
       :searching="$apollo.queries.savedReplies.loading"
       @shown="fetchCommentTemplates"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index 2d088aa2200b8..8b9600b2f07db 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -21,6 +21,7 @@ import { updateText } from '~/lib/utils/text_markdown';
 import ToolbarButton from './toolbar_button.vue';
 import DrawioToolbarButton from './drawio_toolbar_button.vue';
 import CommentTemplatesDropdown from './comment_templates_dropdown.vue';
+import HeaderDivider from './header_divider.vue';
 
 export default {
   components: {
@@ -30,6 +31,7 @@ export default {
     DrawioToolbarButton,
     CommentTemplatesDropdown,
     AiActionsDropdown: () => import('ee_component/ai/components/ai_actions_dropdown.vue'),
+    HeaderDivider,
   },
   directives: {
     GlTooltip: GlTooltipDirective,
@@ -261,249 +263,271 @@ export default {
     <div class="gl-display-flex gl-align-items-center gl-flex-wrap">
       <div
         data-testid="md-header-toolbar"
-        class="md-header-toolbar gl-display-flex gl-py-3 gl-flex-wrap gl-row-gap-3"
+        class="md-header-toolbar gl-display-flex gl-py-3 gl-row-gap-2 gl-flex-grow-1 gl-align-items-flex-start"
       >
-        <gl-button
-          v-if="enablePreview"
-          data-testid="preview-toggle"
-          :value="previewMarkdown ? 'preview' : 'edit'"
-          :label="$options.i18n.previewTabTitle"
-          class="js-md-preview-button gl-flex-direction-row-reverse gl-align-items-center gl-font-weight-normal! gl-mr-2"
-          size="small"
-          category="tertiary"
-          @click="switchPreview"
-          >{{ previewMarkdown ? $options.i18n.hidePreview : $options.i18n.preview }}</gl-button
-        >
-        <template v-if="!previewMarkdown && canSuggest">
+        <div class="gl-display-flex gl-flex-wrap gl-row-gap-2">
+          <gl-button
+            v-if="enablePreview"
+            data-testid="preview-toggle"
+            :value="previewMarkdown ? 'preview' : 'edit'"
+            :label="$options.i18n.previewTabTitle"
+            class="js-md-preview-button gl-flex-direction-row-reverse gl-align-items-center gl-font-weight-normal!"
+            size="small"
+            category="tertiary"
+            @click="switchPreview"
+            >{{ previewMarkdown ? $options.i18n.hidePreview : $options.i18n.preview }}</gl-button
+          >
+          <template v-if="!previewMarkdown && canSuggest">
+            <div class="gl-display-flex gl-row-gap-2">
+              <header-divider :preview-markdown="previewMarkdown" />
+              <toolbar-button
+                ref="suggestButton"
+                :tag="mdSuggestion"
+                :prepend="true"
+                :button-title="__('Insert suggestion')"
+                :cursor-offset="4"
+                :tag-content="lineContent"
+                tracking-property="codeSuggestion"
+                icon="doc-code"
+                data-testid="suggestion-button"
+                class="js-suggestion-btn"
+                @click="handleSuggestDismissed"
+              />
+              <gl-popover
+                v-if="suggestPopoverVisible"
+                :target="$refs.suggestButton.$el"
+                :css-classes="['diff-suggest-popover']"
+                placement="bottom"
+                :show="suggestPopoverVisible"
+                triggers=""
+              >
+                <strong>{{ __('New! Suggest changes directly') }}</strong>
+                <p class="mb-2">
+                  {{
+                    __(
+                      'Suggest code changes which can be immediately applied in one click. Try it out!',
+                    )
+                  }}
+                </p>
+                <gl-button
+                  variant="confirm"
+                  category="primary"
+                  size="small"
+                  data-testid="dismiss-suggestion-popover-button"
+                  @click="handleSuggestDismissed"
+                >
+                  {{ __('Got it') }}
+                </gl-button>
+              </gl-popover>
+            </div>
+          </template>
+          <div class="gl-display-flex gl-row-gap-2">
+            <div
+              v-if="!previewMarkdown && editorAiActions.length"
+              class="gl-display-flex gl-row-gap-2"
+            >
+              <header-divider :preview-markdown="previewMarkdown" />
+              <ai-actions-dropdown
+                :actions="editorAiActions"
+                @input="insertAIAction"
+                @replace="replaceTextarea"
+              />
+            </div>
+            <header-divider :preview-markdown="previewMarkdown" />
+          </div>
+          <toolbar-button
+            v-show="!previewMarkdown"
+            tag="**"
+            :button-title="
+              /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+              sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), {
+                modifierKey,
+              }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
+            "
+            :shortcuts="$options.shortcuts.bold"
+            icon="bold"
+            tracking-property="bold"
+          />
           <toolbar-button
-            ref="suggestButton"
-            :tag="mdSuggestion"
+            v-show="!previewMarkdown"
+            tag="_"
+            :button-title="
+              /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+              sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), {
+                modifierKey,
+              }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
+            "
+            :shortcuts="$options.shortcuts.italic"
+            icon="italic"
+            tracking-property="italic"
+          />
+          <div class="gl-display-flex gl-row-gap-2">
+            <toolbar-button
+              v-if="!restrictedToolBarItems.includes('strikethrough')"
+              v-show="!previewMarkdown"
+              tag="~~"
+              :button-title="
+                /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+                sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}%{shiftKey}X)'), {
+                  modifierKey,
+                  shiftKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
+                })
+              "
+              :shortcuts="$options.shortcuts.strikethrough"
+              icon="strikethrough"
+              tracking-property="strike"
+            />
+            <header-divider :preview-markdown="previewMarkdown" />
+          </div>
+          <toolbar-button
+            v-if="!restrictedToolBarItems.includes('quote')"
+            v-show="!previewMarkdown"
             :prepend="true"
-            :button-title="__('Insert suggestion')"
-            :cursor-offset="4"
-            :tag-content="lineContent"
-            tracking-property="codeSuggestion"
-            icon="doc-code"
-            data-testid="suggestion-button"
-            class="js-suggestion-btn"
-            @click="handleSuggestDismissed"
+            :tag="tag"
+            :button-title="__('Insert a quote')"
+            icon="quote"
+            tracking-property="blockquote"
+            @click="handleQuote"
           />
-          <gl-popover
-            v-if="suggestPopoverVisible"
-            :target="$refs.suggestButton.$el"
-            :css-classes="['diff-suggest-popover']"
-            placement="bottom"
-            :show="suggestPopoverVisible"
-            triggers=""
-          >
-            <strong>{{ __('New! Suggest changes directly') }}</strong>
-            <p class="mb-2">
-              {{
-                __(
-                  'Suggest code changes which can be immediately applied in one click. Try it out!',
-                )
-              }}
-            </p>
-            <gl-button
-              variant="confirm"
-              category="primary"
-              size="small"
-              data-testid="dismiss-suggestion-popover-button"
-              @click="handleSuggestDismissed"
-            >
-              {{ __('Got it') }}
-            </gl-button>
-          </gl-popover>
-        </template>
-        <ai-actions-dropdown
-          v-if="!previewMarkdown && editorAiActions.length"
-          :actions="editorAiActions"
-          @input="insertAIAction"
-          @replace="replaceTextarea"
-        />
-        <toolbar-button
-          v-show="!previewMarkdown"
-          tag="**"
-          :button-title="
-            /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
-            sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), {
-              modifierKey,
-            }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
-          "
-          :shortcuts="$options.shortcuts.bold"
-          icon="bold"
-          tracking-property="bold"
-        />
-        <toolbar-button
-          v-show="!previewMarkdown"
-          tag="_"
-          :button-title="
-            /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
-            sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), {
-              modifierKey,
-            }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
-          "
-          :shortcuts="$options.shortcuts.italic"
-          icon="italic"
-          tracking-property="italic"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('strikethrough')"
-          v-show="!previewMarkdown"
-          tag="~~"
-          :button-title="
-            /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
-            sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}%{shiftKey}X)'), {
-              modifierKey,
-              shiftKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
-            })
-          "
-          :shortcuts="$options.shortcuts.strikethrough"
-          icon="strikethrough"
-          tracking-property="strike"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('quote')"
-          v-show="!previewMarkdown"
-          :prepend="true"
-          :tag="tag"
-          :button-title="__('Insert a quote')"
-          icon="quote"
-          tracking-property="blockquote"
-          @click="handleQuote"
-        />
-        <toolbar-button
-          v-show="!previewMarkdown"
-          tag="`"
-          tag-block="```"
-          :button-title="__('Insert code')"
-          icon="code"
-          tracking-property="code"
-        />
-        <toolbar-button
-          v-show="!previewMarkdown"
-          tag="[{text}](url)"
-          tag-select="url"
-          :button-title="
-            /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
-            sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), {
-              modifierKey,
-            }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
-          "
-          :shortcuts="$options.shortcuts.link"
-          icon="link"
-          tracking-property="link"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('bullet-list')"
-          v-show="!previewMarkdown"
-          :prepend="true"
-          tag="- "
-          :button-title="__('Add a bullet list')"
-          icon="list-bulleted"
-          tracking-property="bulletList"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('numbered-list')"
-          v-show="!previewMarkdown"
-          :prepend="true"
-          tag="1. "
-          :button-title="__('Add a numbered list')"
-          icon="list-numbered"
-          tracking-property="orderedList"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('task-list')"
-          v-show="!previewMarkdown"
-          :prepend="true"
-          tag="- [ ] "
-          :button-title="__('Add a checklist')"
-          icon="list-task"
-          tracking-property="taskList"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('indent')"
-          v-show="!previewMarkdown"
-          class="gl-display-none"
-          :button-title="
-            /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
-            sprintf(s__('MarkdownEditor|Indent line (%{modifierKey}])'), {
-              modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
-            })
-          "
-          :shortcuts="$options.shortcuts.indent"
-          command="indentLines"
-          icon="list-indent"
-          tracking-property="indent"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('outdent')"
-          v-show="!previewMarkdown"
-          class="gl-display-none"
-          :button-title="
-            /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
-            sprintf(s__('MarkdownEditor|Outdent line (%{modifierKey}[)'), {
-              modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
-            })
-          "
-          :shortcuts="$options.shortcuts.outdent"
-          command="outdentLines"
-          icon="list-outdent"
-          tracking-property="outdent"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('collapsible-section')"
-          v-show="!previewMarkdown"
-          :tag="mdCollapsibleSection"
-          :prepend="true"
-          tag-select="Click to expand"
-          :button-title="__('Add a collapsible section')"
-          icon="details-block"
-          tracking-property="details"
-        />
-        <toolbar-button
-          v-if="!restrictedToolBarItems.includes('table')"
-          v-show="!previewMarkdown"
-          :tag="mdTable"
-          :prepend="true"
-          :button-title="__('Add a table')"
-          icon="table"
-          tracking-property="table"
-        />
-        <toolbar-button
-          v-if="!previewMarkdown && !restrictedToolBarItems.includes('attach-file')"
-          data-testid="button-attach-file"
-          data-button-type="attach-file"
-          :button-title="__('Attach a file or image')"
-          icon="paperclip"
-          class="gl-mr-3"
-          tracking-property="upload"
-          @click="handleAttachFile"
-        />
-        <drawio-toolbar-button
-          v-if="!previewMarkdown && drawioEnabled"
-          :uploads-path="uploadsPath"
-          :markdown-preview-path="markdownPreviewPath"
-        />
-        <!-- TODO Add icon and trigger functionality from here -->
-        <toolbar-button
-          v-if="supportsQuickActions"
-          v-show="!previewMarkdown"
-          :prepend="true"
-          tag="/"
-          :button-title="__('Add a quick action')"
-          icon="quick-actions"
-          tracking-property="quickAction"
-        />
-        <comment-templates-dropdown
-          v-if="!previewMarkdown && newCommentTemplatePath"
-          :new-comment-template-path="newCommentTemplatePath"
-          @select="insertSavedReply"
-        />
-        <div v-if="!previewMarkdown" class="full-screen">
+          <toolbar-button
+            v-show="!previewMarkdown"
+            tag="`"
+            tag-block="```"
+            :button-title="__('Insert code')"
+            icon="code"
+            tracking-property="code"
+          />
+          <toolbar-button
+            v-show="!previewMarkdown"
+            tag="[{text}](url)"
+            tag-select="url"
+            :button-title="
+              /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+              sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), {
+                modifierKey,
+              }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
+            "
+            :shortcuts="$options.shortcuts.link"
+            icon="link"
+            tracking-property="link"
+          />
+          <toolbar-button
+            v-if="!restrictedToolBarItems.includes('bullet-list')"
+            v-show="!previewMarkdown"
+            :prepend="true"
+            tag="- "
+            :button-title="__('Add a bullet list')"
+            icon="list-bulleted"
+            tracking-property="bulletList"
+          />
+          <toolbar-button
+            v-if="!restrictedToolBarItems.includes('numbered-list')"
+            v-show="!previewMarkdown"
+            :prepend="true"
+            tag="1. "
+            :button-title="__('Add a numbered list')"
+            icon="list-numbered"
+            tracking-property="orderedList"
+          />
+          <toolbar-button
+            v-if="!restrictedToolBarItems.includes('task-list')"
+            v-show="!previewMarkdown"
+            :prepend="true"
+            tag="- [ ] "
+            :button-title="__('Add a checklist')"
+            icon="list-task"
+            tracking-property="taskList"
+          />
+          <toolbar-button
+            v-if="!restrictedToolBarItems.includes('indent')"
+            v-show="!previewMarkdown"
+            class="gl-display-none"
+            :button-title="
+              /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+              sprintf(s__('MarkdownEditor|Indent line (%{modifierKey}])'), {
+                modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
+              })
+            "
+            :shortcuts="$options.shortcuts.indent"
+            command="indentLines"
+            icon="list-indent"
+            tracking-property="indent"
+          />
+          <toolbar-button
+            v-if="!restrictedToolBarItems.includes('outdent')"
+            v-show="!previewMarkdown"
+            class="gl-display-none"
+            :button-title="
+              /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+              sprintf(s__('MarkdownEditor|Outdent line (%{modifierKey}[)'), {
+                modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
+              })
+            "
+            :shortcuts="$options.shortcuts.outdent"
+            command="outdentLines"
+            icon="list-outdent"
+            tracking-property="outdent"
+          />
+          <div class="gl-display-flex gl-row-gap-2">
+            <toolbar-button
+              v-if="!restrictedToolBarItems.includes('collapsible-section')"
+              v-show="!previewMarkdown"
+              :tag="mdCollapsibleSection"
+              :prepend="true"
+              tag-select="Click to expand"
+              :button-title="__('Add a collapsible section')"
+              icon="details-block"
+              tracking-property="details"
+            />
+            <header-divider :preview-markdown="previewMarkdown" />
+          </div>
+          <toolbar-button
+            v-if="!restrictedToolBarItems.includes('table')"
+            v-show="!previewMarkdown"
+            :tag="mdTable"
+            :prepend="true"
+            :button-title="__('Add a table')"
+            icon="table"
+            tracking-property="table"
+          />
+          <toolbar-button
+            v-if="!previewMarkdown && !restrictedToolBarItems.includes('attach-file')"
+            data-testid="button-attach-file"
+            data-button-type="attach-file"
+            :button-title="__('Attach a file or image')"
+            icon="paperclip"
+            class="gl-mr-2"
+            tracking-property="upload"
+            @click="handleAttachFile"
+          />
+          <drawio-toolbar-button
+            v-if="!previewMarkdown && drawioEnabled"
+            :uploads-path="uploadsPath"
+            :markdown-preview-path="markdownPreviewPath"
+          />
+          <!-- TODO Add icon and trigger functionality from here -->
+          <toolbar-button
+            v-if="supportsQuickActions"
+            v-show="!previewMarkdown"
+            :prepend="true"
+            tag="/"
+            :button-title="__('Add a quick action')"
+            icon="quick-actions"
+            tracking-property="quickAction"
+          />
+          <comment-templates-dropdown
+            v-if="!previewMarkdown && newCommentTemplatePath"
+            :new-comment-template-path="newCommentTemplatePath"
+            @select="insertSavedReply"
+          />
+        </div>
+        <div
+          v-if="!previewMarkdown"
+          class="full-screen gl-flex-grow-1 gl-justify-content-end gl-display-flex"
+        >
           <toolbar-button
             v-if="!restrictedToolBarItems.includes('full-screen')"
-            class="js-zen-enter"
+            class="js-zen-enter gl-mr-0!"
             icon="maximize"
             :button-title="__('Go full screen')"
             :prepend="true"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header_divider.vue b/app/assets/javascripts/vue_shared/components/markdown/header_divider.vue
new file mode 100644
index 0000000000000..d08a3d4cd3449
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/markdown/header_divider.vue
@@ -0,0 +1,16 @@
+<script>
+export default {
+  props: {
+    previewMarkdown: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
+  },
+};
+</script>
+<template>
+  <div v-if="!previewMarkdown" class="md-toolbar-divider gl-display-flex gl-py-2">
+    <div class="gl-border-l gl-pl-3 gl-ml-2"></div>
+  </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
index cf484443c0708..182da7945ffba 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
@@ -111,7 +111,7 @@ export default {
     type="button"
     category="tertiary"
     size="small"
-    class="js-md gl-mr-3"
+    class="js-md gl-mr-2"
     data-container="body"
     @click="$emit('click', $event)"
   />
diff --git a/ee/app/assets/javascripts/ai/components/ai_actions_dropdown.vue b/ee/app/assets/javascripts/ai/components/ai_actions_dropdown.vue
index 3f1663f74ad73..a1eddcb8aec16 100644
--- a/ee/app/assets/javascripts/ai/components/ai_actions_dropdown.vue
+++ b/ee/app/assets/javascripts/ai/components/ai_actions_dropdown.vue
@@ -178,7 +178,7 @@ export default {
         :aria-label="__('AI actions')"
         category="tertiary"
         size="small"
-        class="gl-mr-3 gl-px-2!"
+        class="gl-mr-2 gl-px-2!"
       >
         <gl-loading-icon v-if="loading" />
         <gl-icon v-else name="tanuki" />
-- 
GitLab