Commit f00500d1 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'tr-upload-component' into 'master'

Move design upload component to vue_shared

See merge request gitlab-org/gitlab!46744
parents 95c5e1cf 91322d28
......@@ -5,9 +5,6 @@ export const VALID_DESIGN_FILE_MIMETYPE = {
regex: /image\/.+/,
};
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
export const VALID_DATA_TRANSFER_TYPE = 'Files';
export const ACTIVE_DISCUSSION_SOURCE_TYPES = {
pin: 'pin',
discussion: 'discussion',
......
<script>
import { GlLoadingIcon, GlButton, GlAlert } from '@gitlab/ui';
import { GlLoadingIcon, GlButton, GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import VueDraggable from 'vuedraggable';
import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
import createFlash, { FLASH_TYPES } from '~/flash';
import { s__, sprintf } from '~/locale';
import { __, s__, sprintf } from '~/locale';
import { getFilename } from '~/lib/utils/file_upload';
import UploadButton from '../components/upload/button.vue';
import DeleteButton from '../components/delete_button.vue';
import Design from '../components/list/item.vue';
import DesignDestroyer from '../components/design_destroyer.vue';
import DesignVersionDropdown from '../components/upload/design_version_dropdown.vue';
import DesignDropzone from '../components/upload/design_dropzone.vue';
import DesignDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
import uploadDesignMutation from '../graphql/mutations/upload_design.mutation.graphql';
import moveDesignMutation from '../graphql/mutations/move_design.mutation.graphql';
import allDesignsMixin from '../mixins/all_designs';
......@@ -20,6 +20,7 @@ import {
EXISTING_DESIGN_DROP_MANY_FILES_MESSAGE,
EXISTING_DESIGN_DROP_INVALID_FILENAME_MESSAGE,
MOVE_DESIGN_ERROR,
UPLOAD_DESIGN_INVALID_FILETYPE_ERROR,
designUploadSkippedWarning,
designDeletionError,
} from '../utils/error_messages';
......@@ -34,6 +35,7 @@ import {
} from '../utils/design_management_utils';
import { trackDesignCreate, trackDesignUpdate } from '../utils/tracking';
import { DESIGNS_ROUTE_NAME } from '../router/constants';
import { VALID_DESIGN_FILE_MIMETYPE } from '../constants';
const MAXIMUM_FILE_UPLOAD_LIMIT = 10;
......@@ -42,6 +44,8 @@ export default {
GlLoadingIcon,
GlAlert,
GlButton,
GlSprintf,
GlLink,
UploadButton,
Design,
DesignDestroyer,
......@@ -50,6 +54,11 @@ export default {
DesignDropzone,
VueDraggable,
},
dropzoneProps: {
dropToStartMessage: __('Drop your designs to start your upload.'),
isFileValid: isValidDesignFile,
validFileMimetypes: [VALID_DESIGN_FILE_MIMETYPE.mimetype],
},
mixins: [allDesignsMixin],
apollo: {
permissions: {
......@@ -247,6 +256,9 @@ export default {
const errorMessage = designDeletionError({ singular: this.selectedDesigns.length === 1 });
createFlash({ message: errorMessage });
},
onDesignDropzoneError() {
createFlash({ message: UPLOAD_DESIGN_INVALID_FILETYPE_ERROR });
},
onExistingDesignDropzoneChange(files, existingDesignFilename) {
const filesArr = Array.from(files);
......@@ -325,6 +337,9 @@ export default {
animation: 200,
ghostClass: 'gl-visibility-hidden',
},
i18n: {
dropzoneDescriptionText: __('Drop or %{linkStart}upload%{linkEnd} designs to attach'),
},
};
</script>
......@@ -335,7 +350,11 @@ export default {
@mouseenter="toggleOnPasteListener"
@mouseleave="toggleOffPasteListener"
>
<header v-if="showToolbar" class="row-content-block gl-border-t-0 gl-p-3 gl-display-flex">
<header
v-if="showToolbar"
class="row-content-block gl-border-t-0 gl-p-3 gl-display-flex"
data-testid="design-toolbar-wrapper"
>
<div class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-w-full">
<div>
<span class="gl-font-weight-bold gl-mr-3">{{ s__('DesignManagement|Designs') }}</span>
......@@ -371,7 +390,12 @@ export default {
{{ s__('DesignManagement|Archive selected') }}
</delete-button>
</design-destroyer>
<upload-button v-if="canCreateDesign" :is-saving="isSaving" @upload="onUploadDesign" />
<upload-button
v-if="canCreateDesign"
:is-saving="isSaving"
data-testid="design-upload-button"
@upload="onUploadDesign"
/>
</div>
</div>
</header>
......@@ -414,15 +438,26 @@ export default {
class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone
:has-designs="hasDesigns"
:is-dragging-design="isDraggingDesign"
:display-as-card="hasDesigns"
:enable-drag-behavior="isDraggingDesign"
v-bind="$options.dropzoneProps"
@change="onExistingDesignDropzoneChange($event, design.filename)"
@error="onDesignDropzoneError"
>
<design
v-bind="design"
:is-uploading="isDesignToBeSaved(design.filename)"
class="gl-bg-white"
/>
<template #upload-text="{ openFileUpload }">
<gl-sprintf :message="$options.i18n.dropzoneDescriptionText">
<template #link="{ content }">
<gl-link @click.stop="openFileUpload">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</template>
</design-dropzone>
<input
......@@ -438,12 +473,24 @@ export default {
<template #header>
<li :class="designDropzoneWrapperClass" data-testid="design-dropzone-wrapper">
<design-dropzone
:is-dragging-design="isDraggingDesign"
:enable-drag-behavior="isDraggingDesign"
:class="{ 'design-list-item design-list-item-new': !isDesignListEmpty }"
:has-designs="hasDesigns"
:display-as-card="hasDesigns"
v-bind="$options.dropzoneProps"
data-qa-selector="design_dropzone_content"
@change="onUploadDesign"
/>
@error="onDesignDropzoneError"
>
<template #upload-text="{ openFileUpload }">
<gl-sprintf :message="$options.i18n.dropzoneDescriptionText">
<template #link="{ content }">
<gl-link @click.stop="openFileUpload">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</template>
</design-dropzone>
</li>
</template>
</vue-draggable>
......
// We may wish to make this more restrictive, as per
// https://gitlab.com/gitlab-org/gitlab/issues/118611
export const VALID_IMAGE_FILE_MIMETYPE = {
mimetype: 'image/*',
regex: /image\/.+/,
};
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
export const VALID_DATA_TRANSFER_TYPE = 'Files';
<script>
import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
import createFlash from '~/flash';
import uploadDesignMutation from '../../graphql/mutations/upload_design.mutation.graphql';
import { UPLOAD_DESIGN_INVALID_FILETYPE_ERROR } from '../../utils/error_messages';
import { isValidDesignFile } from '../../utils/design_management_utils';
import { VALID_DATA_TRANSFER_TYPE, VALID_DESIGN_FILE_MIMETYPE } from '../../constants';
import { __ } from '~/locale';
import { isValidImage } from './utils';
import { VALID_DATA_TRANSFER_TYPE, VALID_IMAGE_FILE_MIMETYPE } from './constants';
export default {
components: {
......@@ -13,15 +11,31 @@ export default {
GlSprintf,
},
props: {
hasDesigns: {
displayAsCard: {
type: Boolean,
required: true,
required: false,
default: false,
},
isDraggingDesign: {
enableDragBehavior: {
type: Boolean,
required: false,
default: false,
},
dropToStartMessage: {
type: String,
required: false,
default: __('Drop your files to start your upload.'),
},
isFileValid: {
type: Function,
required: false,
default: isValidImage,
},
validFileMimetypes: {
type: Array,
required: false,
default: () => [VALID_IMAGE_FILE_MIMETYPE.mimetype],
},
},
data() {
return {
......@@ -35,14 +49,17 @@ export default {
},
iconStyles() {
return {
size: this.hasDesigns ? 24 : 16,
class: this.hasDesigns ? 'gl-mb-2' : 'gl-mr-3 gl-text-gray-500',
size: this.displayAsCard ? 24 : 16,
class: this.displayAsCard ? 'gl-mb-2' : 'gl-mr-3 gl-text-gray-500',
};
},
validMimeTypeString() {
return this.validFileMimetypes.join();
},
},
methods: {
isValidUpload(files) {
return files.every(isValidDesignFile);
return files.every(this.isFileValid);
},
isValidDragDataType({ dataTransfer }) {
return Boolean(dataTransfer && dataTransfer.types.some(t => t === VALID_DATA_TRANSFER_TYPE));
......@@ -56,7 +73,7 @@ export default {
const { files } = dataTransfer;
if (!this.isValidUpload(Array.from(files))) {
createFlash({ message: UPLOAD_DESIGN_INVALID_FILETYPE_ERROR });
this.$emit('error');
return;
}
......@@ -72,12 +89,10 @@ export default {
openFileUpload() {
this.$refs.fileUpload.click();
},
onDesignInputChange(e) {
onFileInputChange(e) {
this.$emit('change', e.target.files);
},
},
uploadDesignMutation,
VALID_DESIGN_FILE_MIMETYPE,
};
</script>
......@@ -93,23 +108,25 @@ export default {
>
<slot>
<button
class="card design-dropzone-card design-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
@click="openFileUpload"
>
<div
:class="{ 'gl-flex-direction-column': hasDesigns }"
:class="{ 'gl-flex-direction-column': displayAsCard }"
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-text-center"
data-testid="dropzone-area"
>
<gl-icon name="upload" :size="iconStyles.size" :class="iconStyles.class" />
<p class="gl-mb-0">
<gl-sprintf :message="__('Drop or %{linkStart}upload%{linkEnd} designs to attach')">
<template #link="{ content }">
<gl-link @click.stop="openFileUpload">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
<slot name="upload-text" :openFileUpload="openFileUpload">
<gl-sprintf :message="__('Drop or %{linkStart}upload%{linkEnd} files to attach')">
<template #link="{ content }">
<gl-link @click.stop="openFileUpload">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</slot>
</p>
</div>
</button>
......@@ -117,29 +134,37 @@ export default {
<input
ref="fileUpload"
type="file"
name="design_file"
:accept="$options.VALID_DESIGN_FILE_MIMETYPE.mimetype"
name="upload_file"
:accept="validFileMimetypes"
class="hide"
multiple
@change="onDesignInputChange"
@change="onFileInputChange"
/>
</slot>
<transition name="design-dropzone-fade">
<transition name="upload-dropzone-fade">
<div
v-show="dragging && !isDraggingDesign"
class="card design-dropzone-border design-dropzone-overlay gl-w-full gl-h-full gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-3 gl-bg-white"
v-show="dragging && !enableDragBehavior"
class="card upload-dropzone-border upload-dropzone-overlay gl-w-full gl-h-full gl-absolute gl-display-flex gl-align-items-center gl-justify-content-center gl-p-3 gl-bg-white"
>
<div v-show="!isDragDataValid" class="mw-50 gl-text-center">
<h3 :class="{ 'gl-font-base gl-display-inline': !hasDesigns }">{{ __('Oh no!') }}</h3>
<span>{{
__(
'You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico.',
)
}}</span>
<slot name="invalidDragDataSlot">
<h3 :class="{ 'gl-font-base gl-display-inline': !displayAsCard }">
{{ __('Oh no!') }}
</h3>
<span>{{
__(
'You are trying to upload something other than an image. Please upload a .png, .jpg, .jpeg, .gif, .bmp, .tiff or .ico.',
)
}}</span>
</slot>
</div>
<div v-show="isDragDataValid" class="mw-50 gl-text-center">
<h3 :class="{ 'gl-font-base gl-display-inline': !hasDesigns }">{{ __('Incoming!') }}</h3>
<span>{{ __('Drop your designs to start your upload.') }}</span>
<slot name="validDragDataSlot">
<h3 :class="{ 'gl-font-base gl-display-inline': !displayAsCard }">
{{ __('Incoming!') }}
</h3>
<span>{{ dropToStartMessage }}</span>
</slot>
</div>
</div>
</transition>
......
import { VALID_IMAGE_FILE_MIMETYPE } from './constants';
export const isValidImage = ({ type }) =>
(type.match(VALID_IMAGE_FILE_MIMETYPE.regex) || []).length > 0;
......@@ -181,41 +181,3 @@ $t-gray-a-16-design-pin: rgba($black, 0.16);
.design-card-header {
background: transparent;
}
.design-dropzone-border {
border: 2px dashed $gray-100;
}
.design-dropzone-card {
transition: border $gl-transition-duration-medium $general-hover-transition-curve;
color: $gl-text-color;
&:focus,
&:active {
outline: none;
border: 2px dashed $purple;
color: $gl-text-color;
}
&:hover {
border-color: $gray-300;
}
}
.design-dropzone-overlay {
border: 2px dashed $purple;
top: 0;
left: 0;
pointer-events: none;
opacity: 1;
}
.design-dropzone-fade-enter-active,
.design-dropzone-fade-leave-active {
transition: opacity $general-hover-transition-duration $general-hover-transition-curve;
}
.design-dropzone-fade-enter,
.design-dropzone-fade-leave-to {
opacity: 0;
}
.upload-dropzone-border {
border: 2px dashed $gray-100;
}
.upload-dropzone-card {
transition: border $gl-transition-duration-medium $general-hover-transition-curve;
color: $gl-text-color;
&:focus,
&:active {
outline: none;
border: 2px dashed $purple;
color: $gl-text-color;
}
&:hover {
border-color: $gray-300;
}
}
.upload-dropzone-overlay {
border: 2px dashed $purple;
top: 0;
left: 0;
pointer-events: none;
opacity: 1;
}
.upload-dropzone-fade-enter-active,
.upload-dropzone-fade-leave-active {
transition: opacity $general-hover-transition-duration $general-hover-transition-curve;
}
.upload-dropzone-fade-enter,
.upload-dropzone-fade-leave-to {
opacity: 0;
}
......@@ -9757,9 +9757,15 @@ msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} designs to attach"
msgstr ""
msgid "Drop or %{linkStart}upload%{linkEnd} files to attach"
msgstr ""
msgid "Drop your designs to start your upload."
msgstr ""
msgid "Drop your files to start your upload."
msgstr ""
msgid "Due Date"
msgstr ""
......
......@@ -51,7 +51,7 @@ RSpec.describe 'User uploads new design', :js do
end
def upload_design(fixture, count:)
attach_file(:design_file, fixture, match: :first, make_visible: true)
attach_file(:upload_file, fixture, match: :first, make_visible: true)
wait_for('designs uploaded') do
issue.reload.designs.count == count
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Design management index page designs does not render toolbar when there is no permission 1`] = `
<div
class="gl-mt-5"
data-testid="designs-root"
>
<!---->
<div
class="gl-mt-6"
>
<ol
class="list-unstyled row"
>
<li
class="gl-flex-direction-column col-md-6 col-lg-3 gl-mb-3"
data-testid="design-dropzone-wrapper"
>
<design-dropzone-stub
class="design-list-item design-list-item-new"
data-qa-selector="design_dropzone_content"
hasdesigns="true"
/>
</li>
<li
class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone-stub
hasdesigns="true"
>
<design-stub
class="gl-bg-white"
event="NONE"
filename="design-1-name"
id="design-1"
image="design-1-image"
notescount="0"
/>
</design-dropzone-stub>
<!---->
</li>
<li
class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone-stub
hasdesigns="true"
>
<design-stub
class="gl-bg-white"
event="NONE"
filename="design-2-name"
id="design-2"
image="design-2-image"
notescount="1"
/>
</design-dropzone-stub>
<!---->
</li>
<li
class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone-stub
hasdesigns="true"
>
<design-stub
class="gl-bg-white"
event="NONE"
filename="design-3-name"
id="design-3"
image="design-3-image"
notescount="0"
/>
</design-dropzone-stub>
<!---->
</li>
</ol>
</div>
<router-view-stub
name="default"
/>
</div>
`;
exports[`Design management index page designs renders designs list and header with upload button 1`] = `
<div
class="gl-mt-5"
data-testid="designs-root"
>
<header
class="row-content-block gl-border-t-0 gl-p-3 gl-display-flex"
>
<div
class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-w-full"
>
<div>
<span
class="gl-font-weight-bold gl-mr-3"
>
Designs
</span>
<design-version-dropdown-stub />
</div>
<div
class="qa-selector-toolbar gl-display-flex gl-align-items-center"
>
<gl-button-stub
buttontextclasses=""
category="primary"
class="gl-mr-4 js-select-all"
icon=""
size="small"
variant="link"
>
Select all
</gl-button-stub>
<div>
<delete-button-stub
buttoncategory="secondary"
buttonclass="gl-mr-3"
buttonsize="small"
buttonvariant="warning"
data-qa-selector="archive_button"
>
Archive selected
</delete-button-stub>
</div>
<upload-button-stub />
</div>
</div>
</header>
<div
class="gl-mt-6"
>
<ol
class="list-unstyled row"
>
<li
class="gl-flex-direction-column col-md-6 col-lg-3 gl-mb-3"
data-testid="design-dropzone-wrapper"
>
<design-dropzone-stub
class="design-list-item design-list-item-new"
data-qa-selector="design_dropzone_content"
hasdesigns="true"
/>
</li>
<li
class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone-stub
hasdesigns="true"
>
<design-stub
class="gl-bg-white"
event="NONE"
filename="design-1-name"
id="design-1"
image="design-1-image"
notescount="0"
/>
</design-dropzone-stub>
<input
class="design-checkbox"
data-qa-design="design-1-name"
data-qa-selector="design_checkbox"
type="checkbox"
/>
</li>
<li
class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone-stub
hasdesigns="true"
>
<design-stub
class="gl-bg-white"
event="NONE"
filename="design-2-name"
id="design-2"
image="design-2-image"
notescount="1"
/>
</design-dropzone-stub>
<input
class="design-checkbox"
data-qa-design="design-2-name"
data-qa-selector="design_checkbox"
type="checkbox"
/>
</li>
<li
class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone-stub
hasdesigns="true"
>
<design-stub
class="gl-bg-white"
event="NONE"
filename="design-3-name"
id="design-3"
image="design-3-image"
notescount="0"
/>
</design-dropzone-stub>
<input
class="design-checkbox"
data-qa-design="design-3-name"
data-qa-selector="design_checkbox"
type="checkbox"
/>
</li>
</ol>
</div>
<router-view-stub
name="default"
/>
</div>
`;
exports[`Design management index page designs renders error 1`] = `
<div
class="gl-mt-5"
......@@ -288,34 +53,3 @@ exports[`Design management index page designs renders loading icon 1`] = `
/>
</div>
`;
exports[`Design management index page when has no designs renders design dropzone 1`] = `
<div
class="gl-mt-5"
data-testid="designs-root"
>
<!---->
<div
class="gl-mt-6"
>
<ol
class="list-unstyled row"
>
<li
class="col-12"
data-testid="design-dropzone-wrapper"
>
<design-dropzone-stub
class=""
data-qa-selector="design_dropzone_content"
/>
</li>
</ol>
</div>
<router-view-stub
name="default"
/>
</div>
`;
......@@ -10,7 +10,7 @@ import permissionsQuery from 'shared_queries/design_management/design_permission
import Index from '~/design_management/pages/index.vue';
import uploadDesignQuery from '~/design_management/graphql/mutations/upload_design.mutation.graphql';
import DesignDestroyer from '~/design_management/components/design_destroyer.vue';
import DesignDropzone from '~/design_management/components/upload/design_dropzone.vue';
import DesignDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
import DeleteButton from '~/design_management/components/delete_button.vue';
import Design from '~/design_management/components/list/item.vue';
import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants';
......@@ -105,6 +105,8 @@ describe('Design management index page', () => {
const findDesignsWrapper = () => wrapper.find('[data-testid="designs-root"]');
const findDesigns = () => wrapper.findAll(Design);
const draggableAttributes = () => wrapper.find(VueDraggable).vm.$attrs;
const findDesignUploadButton = () => wrapper.find('[data-testid="design-upload-button"]');
const findDesignToolbarWrapper = () => wrapper.find('[data-testid="design-toolbar-wrapper"]');
async function moveDesigns(localWrapper) {
await jest.runOnlyPendingTimers();
......@@ -214,13 +216,17 @@ describe('Design management index page', () => {
it('renders designs list and header with upload button', () => {
createComponent({ allVersions: [mockVersion] });
expect(wrapper.element).toMatchSnapshot();
expect(findDesignsWrapper().exists()).toBe(true);
expect(findDesigns().length).toBe(3);
expect(findDesignToolbarWrapper().exists()).toBe(true);
expect(findDesignUploadButton().exists()).toBe(true);
});
it('does not render toolbar when there is no permission', () => {
createComponent({ designs: mockDesigns, allVersions: [mockVersion], createDesign: false });
expect(wrapper.element).toMatchSnapshot();
expect(findDesignToolbarWrapper().exists()).toBe(false);
expect(findDesignUploadButton().exists()).toBe(false);
});
it('has correct classes applied to design dropzone', () => {
......@@ -247,7 +253,7 @@ describe('Design management index page', () => {
it('renders design dropzone', () =>
wrapper.vm.$nextTick().then(() => {
expect(wrapper.element).toMatchSnapshot();
expect(findDropzone().exists()).toBe(true);
}));
it('has correct classes applied to design dropzone', () => {
......
import { shallowMount } from '@vue/test-utils';
import { GlIcon } from '@gitlab/ui';
import DesignDropzone from '~/design_management/components/upload/design_dropzone.vue';
import createFlash from '~/flash';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
jest.mock('~/flash');
describe('Design management dropzone component', () => {
describe('Upload dropzone component', () => {
let wrapper;
const mockDragEvent = ({ types = ['Files'], files = [] }) => {
return { dataTransfer: { types, files } };
};
const findDropzoneCard = () => wrapper.find('.design-dropzone-card');
const findDropzoneCard = () => wrapper.find('.upload-dropzone-card');
const findDropzoneArea = () => wrapper.find('[data-testid="dropzone-area"]');
const findIcon = () => wrapper.find(GlIcon);
function createComponent({ slots = {}, data = {}, props = {} } = {}) {
wrapper = shallowMount(DesignDropzone, {
wrapper = shallowMount(UploadDropzone, {
slots,
propsData: {
hasDesigns: true,
displayAsCard: true,
...props,
},
data() {
......@@ -126,28 +125,50 @@ describe('Design management dropzone component', () => {
expect(wrapper.emitted().change[0]).toEqual([[mockFile]]);
});
it('calls createFlash when files are invalid', () => {
it('emits error event when files are invalid', () => {
createComponent({ data: mockData });
const mockEvent = mockDragEvent({ files: [{ type: 'audio/midi' }] });
wrapper.vm.ondrop(mockEvent);
expect(wrapper.emitted()).toHaveProperty('error');
});
it('allows validation function to be overwritten', () => {
createComponent({ data: mockData, props: { isFileValid: () => true } });
const mockEvent = mockDragEvent({ files: [{ type: 'audio/midi' }] });
wrapper.vm.ondrop(mockEvent);
expect(createFlash).toHaveBeenCalledTimes(1);
expect(wrapper.emitted()).not.toHaveProperty('error');
});
});
});
it('applies correct classes when there are no designs or no design saving loader', () => {
createComponent({ props: { hasDesigns: false } });
it('applies correct classes when displaying as a standalone item', () => {
createComponent({ props: { displayAsCard: false } });
expect(findDropzoneArea().classes()).not.toContain('gl-flex-direction-column');
expect(findIcon().classes()).toEqual(['gl-mr-3', 'gl-text-gray-500']);
expect(findIcon().props('size')).toBe(16);
});
it('applies correct classes when there are designs or design saving loader', () => {
createComponent({ props: { hasDesigns: true } });
it('applies correct classes when displaying in card mode', () => {
createComponent({ props: { displayAsCard: true } });
expect(findDropzoneArea().classes()).toContain('gl-flex-direction-column');
expect(findIcon().classes()).toEqual(['gl-mb-2']);
expect(findIcon().props('size')).toBe(24);
});
it('correctly overrides description and drop messages', () => {
createComponent({
props: {
dropToStartMessage: 'Test drop-to-start message.',
validFileMimetypes: ['image/jpg', 'image/jpeg'],
},
slots: {
'upload-text': '<span>Test %{linkStart}description%{linkEnd} message.</span>',
},
});
expect(wrapper.element).toMatchSnapshot();
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment