Skip to content
Snippets Groups Projects
Commit 9c6fade7 authored by Kushal Pandya's avatar Kushal Pandya
Browse files

Update UX for attaching files in GFM input fields

Move attach file button to GFM toolbar and show
only upload progress in status bar.

Changelog: changed
parent 65b47fcf
No related branches found
No related tags found
1 merge request!92680Update UX for attaching files in GFM input fields
Showing
with 53 additions and 47 deletions
......@@ -28,7 +28,6 @@ function getErrorMessage(res) {
export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
const divHover = '<div class="div-dropzone-hover"></div>';
const iconPaperclip = spriteIcon('paperclip', 'div-dropzone-icon s24');
const $attachButton = form.find('.button-attach-file');
const $attachingFileMessage = form.find('.attaching-file-message');
const $cancelButton = form.find('.button-cancel-uploading-files');
const $retryLink = form.find('.retry-uploading-link');
......@@ -89,8 +88,6 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
const shouldPad = processingFileCount >= 1;
pasteText(response.link.markdown, shouldPad);
// Show 'Attach a file' link only when all files have been uploaded.
if (!processingFileCount) $attachButton.removeClass('hide');
addFileToForm(response.link.url);
},
error: (file, errorMessage = __('Attaching the file failed.'), xhr) => {
......@@ -104,7 +101,6 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
$uploadingErrorContainer.removeClass('hide');
$uploadingErrorMessage.html(message);
$attachButton.addClass('hide');
$cancelButton.addClass('hide');
},
totaluploadprogress(totalUploadProgress) {
......@@ -115,13 +111,11 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
// DOM elements already exist.
// Instead of dynamically generating them,
// we just either hide or show them.
$attachButton.addClass('hide');
$uploadingErrorContainer.addClass('hide');
$uploadingProgressContainer.removeClass('hide');
$cancelButton.removeClass('hide');
},
removedfile: () => {
$attachButton.removeClass('hide');
$cancelButton.addClass('hide');
$uploadingProgressContainer.addClass('hide');
$uploadingErrorContainer.addClass('hide');
......@@ -282,11 +276,18 @@ export default function dropzoneInput(form, config = { parallelUploads: 2 }) {
messageContainer.text(`${attachingMessage} -`);
};
form.find('.markdown-selector').click(function onMarkdownClick(e) {
function handleAttachFile(e) {
e.preventDefault();
$(this).closest('.gfm-form').find('.div-dropzone').click();
formTextarea.focus();
});
}
form.find('.markdown-selector').click(handleAttachFile);
const $attachFileButton = form.find('.js-attach-file-button');
if ($attachFileButton.length) {
$attachFileButton.get(0).addEventListener('click', handleAttachFile);
}
return $formDropzone.get(0) ? Dropzone.forElement($formDropzone.get(0)) : null;
}
......@@ -156,6 +156,14 @@ export default {
})
.catch(() => {});
},
handleAttachFile(e) {
e.preventDefault();
const $gfmForm = $(this.$el).closest('.gfm-form');
const $gfmTextarea = $gfmForm.find('.js-gfm-input');
$gfmForm.find('.div-dropzone').click();
$gfmTextarea.focus();
},
},
shortcuts: {
bold: keysFor(BOLD_TEXT),
......@@ -324,6 +332,14 @@ export default {
:button-title="__('Add a table')"
icon="table"
/>
<toolbar-button
v-if="!restrictedToolBarItems.includes('attach-file')"
data-testid="button-attach-file"
:prepend="true"
:button-title="__('Attach a file or image')"
icon="paperclip"
@click="handleAttachFile"
/>
<toolbar-button
v-if="!restrictedToolBarItems.includes('full-screen')"
class="js-zen-enter"
......
......@@ -74,7 +74,7 @@ export default {
</div>
<span v-if="canAttachFile" class="uploading-container">
<span class="uploading-progress-container hide">
<gl-icon name="media" />
<gl-icon name="paperclip" />
<span class="attaching-file-message"></span>
<!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
<span class="uploading-progress">0%</span>
......@@ -82,7 +82,7 @@ export default {
</span>
<span class="uploading-error-container hide">
<span class="uploading-error-icon">
<gl-icon name="media" />
<gl-icon name="paperclip" />
</span>
<span class="uploading-error-message"></span>
......@@ -113,14 +113,6 @@ export default {
</template>
</gl-sprintf>
</span>
<gl-button
icon="media"
variant="link"
category="primary"
class="markdown-selector button-attach-file gl-vertical-align-text-bottom"
>
{{ __('Attach a file') }}
</gl-button>
<gl-button
variant="link"
category="primary"
......
......@@ -88,6 +88,6 @@ export default {
category="tertiary"
class="js-md"
data-container="body"
@click="() => $emit('click')"
@click="$emit('click', $event)"
/>
</template>
......@@ -266,9 +266,10 @@ def strip_empty_link_tags(text)
def markdown_toolbar_button(options = {})
data = options[:data].merge({ container: 'body' })
css_classes = %w[gl-button btn btn-default-tertiary btn-icon js-md has-tooltip] << options[:css_class].to_s
content_tag :button,
type: 'button',
class: 'gl-button btn btn-default-tertiary btn-icon js-md has-tooltip',
class: css_classes.join(' '),
data: data,
title: options[:title],
aria: { label: options[:title] } do
......
......@@ -27,6 +27,10 @@
data: { "md-tag" => "<details><summary>Click to expand</summary>\n{text}\n</details>", "md-prepend" => true, "md-select" => "Click to expand" },
title: _("Add a collapsible section") })
= markdown_toolbar_button({ icon: "table", data: { "md-tag" => "| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |", "md-prepend" => true }, title: _("Add a table") })
= markdown_toolbar_button({ icon: "paperclip",
data: { "md-tag" => "", "md-prepend" => true, "testid" => "button-attach-file" },
css_class: 'js-attach-file-button markdown-selector',
title: _("Attach a file or image") })
- if show_fullscreen_button
%button.gl-button.btn.btn-default-tertiary.btn-icon.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: _("Go full screen"), data: { container: "body" } }
= sprite_icon("maximize")
......@@ -11,7 +11,7 @@
- if supports_file_upload
%span.uploading-container
%span.uploading-progress-container.hide
= sprite_icon('media', css_class: 'gl-icon gl-vertical-align-text-bottom')
= sprite_icon('paperclip', css_class: 'gl-icon gl-vertical-align-text-bottom')
%span.attaching-file-message
-# Populated by app/assets/javascripts/dropzone_input.js
%span.uploading-progress 0%
......@@ -19,7 +19,7 @@
%span.uploading-error-container.hide
%span.uploading-error-icon
= sprite_icon('media', css_class: 'gl-icon gl-vertical-align-text-bottom')
= sprite_icon('paperclip', css_class: 'gl-icon gl-vertical-align-text-bottom')
%span.uploading-error-message
-# Populated by app/assets/javascripts/dropzone_input.js
%button.btn.gl-button.btn-link.gl-vertical-align-baseline.retry-uploading-link
......@@ -31,11 +31,6 @@
= _("attach a new file")
= _(".")
%button.btn.gl-button.btn-link.button-attach-file.markdown-selector.button-attach-file.gl-vertical-align-text-bottom
= sprite_icon('media')
%span.gl-button-text
= _("Attach a file")
%button.btn.gl-button.btn-link.button-cancel-uploading-files.gl-vertical-align-baseline.hide
%span.gl-button-text
= _("Cancel")
......@@ -5233,7 +5233,7 @@ msgstr ""
msgid "At risk"
msgstr ""
 
msgid "Attach a file"
msgid "Attach a file or image"
msgstr ""
 
msgid "Attaching File - %{progress}"
......@@ -151,7 +151,7 @@
click_button 'Cancel'
end
expect(page).to have_button('Attach a file')
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_button('Cancel')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
......
......@@ -103,9 +103,9 @@
end
end
it 'release notes form shows "Attach a file" button', :js do
it 'release notes form shows "Attach a file or image" button', :js do
page.within('.content form.release-form') do
expect(page).to have_button('Attach a file')
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
end
......
......@@ -16,8 +16,8 @@
end
context 'before uploading' do
it 'shows "Attach a file" button', :js do
expect(page).to have_button('Attach a file')
it 'shows "Attach a file or image" button', :js do
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
end
......@@ -30,7 +30,7 @@
click_button 'Cancel'
end
expect(page).to have_button('Attach a file')
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_button('Cancel')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
......@@ -60,16 +60,15 @@
expect(page).to have_selector('.uploading-error-message', visible: true, text: error_text)
expect(page).to have_button('Try again', visible: true)
expect(page).to have_button('attach a new file', visible: true)
expect(page).not_to have_button('Attach a file')
end
end
context 'uploading is complete' do
it 'shows "Attach a file" button on uploading complete', :js do
it 'shows "Attach a file or image" button on uploading complete', :js do
dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')])
wait_for_requests
expect(page).to have_button('Attach a file')
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
......
......@@ -76,7 +76,7 @@ describe('Markdown field component', () => {
const getMarkdownButton = () => subject.find('.js-md');
const getListBulletedButton = () => subject.findAll('.js-md[title="Add a bullet list"]');
const getVideo = () => subject.find('video');
const getAttachButton = () => subject.find('.button-attach-file');
const getAttachButton = () => subject.findByTestId('button-attach-file');
const clickAttachButton = () => getAttachButton().trigger('click');
const findDropzone = () => subject.find('.div-dropzone');
const findMarkdownHeader = () => subject.findComponent(MarkdownFieldHeader);
......@@ -232,13 +232,10 @@ describe('Markdown field component', () => {
});
});
it('should render attach a file button', () => {
expect(getAttachButton().text()).toBe('Attach a file');
});
it('should trigger dropzone when attach button is clicked', () => {
expect(dropzoneSpy).not.toHaveBeenCalled();
getAttachButton().trigger('click');
clickAttachButton();
expect(dropzoneSpy).toHaveBeenCalled();
......
......@@ -56,6 +56,7 @@ describe('Markdown field header component', () => {
'Add a task list',
'Add a collapsible section',
'Add a table',
'Attach a file or image',
'Go full screen',
];
const elements = findToolbarButtons();
......
......@@ -12,8 +12,8 @@ def attach_with_dropzone(wait = false)
end
context 'before uploading' do
it 'shows "Attach a file" button' do
expect(page).to have_button('Attach a file')
it 'shows "Attach a file or image" button' do
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
end
......@@ -26,7 +26,7 @@ def attach_with_dropzone(wait = false)
click_button 'Cancel'
end
expect(page).to have_button('Attach a file')
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_button('Cancel')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
......@@ -41,11 +41,11 @@ def attach_with_dropzone(wait = false)
end
context 'uploading is complete' do
it 'shows "Attach a file" button on uploading complete' do
it 'shows "Attach a file or image" button on uploading complete' do
attach_with_dropzone
wait_for_requests
expect(page).to have_button('Attach a file')
expect(page).to have_selector('[data-testid="button-attach-file"]')
expect(page).not_to have_selector('.uploading-progress-container', visible: true)
end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment