Commit 3896d1c3 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '12928-design-management-displays-broken-image-while-uploading' into 'master'

Show loading spinner in design card while design is uploading

See merge request gitlab-org/gitlab!20814
parents d7b8bd27 59328941
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import Timeago from '~/vue_shared/components/time_ago_tooltip.vue';
import { n__, __ } from '~/locale';
export default {
components: {
GlLoadingIcon,
Icon,
Timeago,
},
......@@ -34,6 +36,11 @@ export default {
required: false,
default: null,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
icon() {
......@@ -74,13 +81,15 @@ export default {
}"
class="card cursor-pointer text-plain js-design-list-item design-list-item"
>
<div class="card-body p-0 d-flex align-items-center overflow-hidden position-relative">
<div class="card-body p-0 d-flex-center overflow-hidden position-relative">
<div v-if="icon.name" class="design-event position-absolute">
<span :title="icon.tooltip" :aria-label="icon.tooltip">
<icon :name="icon.name" :size="18" :class="icon.classes" />
</span>
</div>
<gl-loading-icon v-if="isLoading" size="md" />
<img
v-else
:src="image"
:alt="filename"
class="block ml-auto mr-auto mw-100 mh-100 design-img"
......
......@@ -46,7 +46,7 @@ export default {
permissions: {
createDesign: false,
},
isSaving: false,
filesToBeSaved: [],
selectedDesigns: [],
};
},
......@@ -54,6 +54,9 @@ export default {
isLoading() {
return this.$apollo.queries.designs.loading || this.$apollo.queries.permissions.loading;
},
isSaving() {
return this.filesToBeSaved.length > 0;
},
canCreateDesign() {
return this.permissions.createDesign;
},
......@@ -100,7 +103,8 @@ export default {
return null;
}
const optimisticResponse = Array.from(files).map(file => ({
this.filesToBeSaved = Array.from(files);
const optimisticResponse = this.filesToBeSaved.map(file => ({
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
__typename: 'Design',
......@@ -133,8 +137,6 @@ export default {
},
}));
this.isSaving = true;
return this.$apollo
.mutate({
mutation: uploadDesignMutation,
......@@ -167,7 +169,7 @@ export default {
throw e;
})
.finally(() => {
this.isSaving = false;
this.filesToBeSaved = [];
});
},
changeSelectedDesigns(filename) {
......@@ -187,6 +189,12 @@ export default {
isDesignSelected(filename) {
return this.selectedDesigns.includes(filename);
},
isDesignToBeSaved(filename) {
return this.filesToBeSaved.some(file => file.name === filename);
},
canSelectDesign(filename) {
return this.isLatestVersion && this.canCreateDesign && !this.isDesignToBeSaved(filename);
},
onDesignDelete() {
this.selectedDesigns = [];
if (this.$route.query.version) this.$router.push({ name: 'designs' });
......@@ -241,9 +249,9 @@ export default {
</div>
<ol v-else-if="hasDesigns" class="list-unstyled row">
<li v-for="design in designs" :key="design.id" class="col-md-6 col-lg-4 mb-3">
<design v-bind="design" />
<design v-bind="design" :is-loading="isDesignToBeSaved(design.filename)" />
<input
v-if="isLatestVersion && canCreateDesign"
v-if="canSelectDesign(design.filename)"
:checked="isDesignSelected(design.filename)"
type="checkbox"
class="design-checkbox"
......
---
title: Show loading spinner in design card while design is uploading
merge_request: 20814
author:
type: added
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Design management list item component hides comment count 1`] = `
exports[`Design management list item component with no notes renders item with correct status icon for creation event 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex align-items-center overflow-hidden position-relative"
>
<!---->
<img
alt="test"
class="block ml-auto mr-auto mw-100 mh-100 design-img"
data-qa-selector="design_image"
src="http://via.placeholder.com/300"
/>
</div>
<div
class="card-footer d-flex w-100"
>
<div
class="d-flex flex-column str-truncated-100"
>
<span
class="bold str-truncated-100"
data-qa-selector="design_file_name"
>
test
</span>
<span
class="str-truncated-100"
>
Updated
<timeago-stub
cssclass=""
time="01-01-2019"
tooltipplacement="bottom"
/>
</span>
</div>
<!---->
</div>
</router-link-stub>
`;
exports[`Design management list item component renders item with correct status icon for creation event 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex align-items-center overflow-hidden position-relative"
class="card-body p-0 d-flex-center overflow-hidden position-relative"
>
<div
class="design-event position-absolute"
......@@ -111,13 +62,13 @@ exports[`Design management list item component renders item with correct status
</router-link-stub>
`;
exports[`Design management list item component renders item with correct status icon for deletion event 1`] = `
exports[`Design management list item component with no notes renders item with correct status icon for deletion event 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex align-items-center overflow-hidden position-relative"
class="card-body p-0 d-flex-center overflow-hidden position-relative"
>
<div
class="design-event position-absolute"
......@@ -173,13 +124,13 @@ exports[`Design management list item component renders item with correct status
</router-link-stub>
`;
exports[`Design management list item component renders item with correct status icon for modification event 1`] = `
exports[`Design management list item component with no notes renders item with correct status icon for modification event 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex align-items-center overflow-hidden position-relative"
class="card-body p-0 d-flex-center overflow-hidden position-relative"
>
<div
class="design-event position-absolute"
......@@ -235,13 +186,13 @@ exports[`Design management list item component renders item with correct status
</router-link-stub>
`;
exports[`Design management list item component renders item with multiple comments 1`] = `
exports[`Design management list item component with notes renders item with multiple comments 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex align-items-center overflow-hidden position-relative"
class="card-body p-0 d-flex-center overflow-hidden position-relative"
>
<!---->
......@@ -301,13 +252,13 @@ exports[`Design management list item component renders item with multiple commen
</router-link-stub>
`;
exports[`Design management list item component renders item with no status icon for none event 1`] = `
exports[`Design management list item component with no notes renders item with no status icon for none event 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex align-items-center overflow-hidden position-relative"
class="card-body p-0 d-flex-center overflow-hidden position-relative"
>
<!---->
......@@ -350,13 +301,13 @@ exports[`Design management list item component renders item with no status icon
</router-link-stub>
`;
exports[`Design management list item component renders item with single comment 1`] = `
exports[`Design management list item component with notes renders item with single comment 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex align-items-center overflow-hidden position-relative"
class="card-body p-0 d-flex-center overflow-hidden position-relative"
>
<!---->
......@@ -415,3 +366,51 @@ exports[`Design management list item component renders item with single comment
</div>
</router-link-stub>
`;
exports[`Design management list item component with no notes renders loading spinner when isLoading is true 1`] = `
<router-link-stub
class="card cursor-pointer text-plain js-design-list-item design-list-item"
to="[object Object]"
>
<div
class="card-body p-0 d-flex-center overflow-hidden position-relative"
>
<!---->
<glloadingicon-stub
color="orange"
label="Loading"
size="md"
/>
</div>
<div
class="card-footer d-flex w-100"
>
<div
class="d-flex flex-column str-truncated-100"
>
<span
class="bold str-truncated-100"
data-qa-selector="design_file_name"
>
test
</span>
<span
class="str-truncated-100"
>
Updated
<timeago-stub
cssclass=""
time="01-01-2019"
tooltipplacement="bottom"
/>
</span>
</div>
<!---->
</div>
</router-link-stub>
`;
......@@ -6,10 +6,21 @@ const localVue = createLocalVue();
localVue.use(VueRouter);
const router = new VueRouter();
// Referenced from: doc/api/graphql/reference/gitlab_schema.graphql:DesignVersionEvent
const DESIGN_VERSION_EVENT = {
CREATION: 'CREATION',
DELETION: 'DELETION',
MODIFICATION: 'MODIFICATION',
NO_CHANGE: 'NONE',
};
describe('Design management list item component', () => {
let wrapper;
function createComponent(notesCount = 1, event = 'NONE') {
function createComponent({
notesCount = 0,
event = DESIGN_VERSION_EVENT.NO_CHANGE,
isLoading = false,
} = {}) {
wrapper = shallowMount(Item, {
sync: false,
localVue,
......@@ -18,6 +29,7 @@ describe('Design management list item component', () => {
id: 1,
filename: 'test',
image: 'http://via.placeholder.com/300',
isLoading,
event,
notesCount,
updatedAt: '01-01-2019',
......@@ -30,45 +42,49 @@ describe('Design management list item component', () => {
wrapper.destroy();
});
it('renders item with single comment', () => {
createComponent();
describe('with notes', () => {
it('renders item with single comment', () => {
createComponent({ notesCount: 1 });
expect(wrapper.element).toMatchSnapshot();
});
expect(wrapper.element).toMatchSnapshot();
});
it('renders item with multiple comments', () => {
createComponent(2);
it('renders item with multiple comments', () => {
createComponent({ notesCount: 2 });
expect(wrapper.element).toMatchSnapshot();
expect(wrapper.element).toMatchSnapshot();
});
});
it('hides comment count', () => {
createComponent(0);
describe('with no notes', () => {
it('renders item with no status icon for none event', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
expect(wrapper.element).toMatchSnapshot();
});
it('renders item with correct status icon for modification event', () => {
createComponent(0, 'MODIFICATION');
it('renders item with correct status icon for modification event', () => {
createComponent({ event: DESIGN_VERSION_EVENT.MODIFICATION });
expect(wrapper.element).toMatchSnapshot();
});
expect(wrapper.element).toMatchSnapshot();
});
it('renders item with correct status icon for deletion event', () => {
createComponent(0, 'DELETION');
it('renders item with correct status icon for deletion event', () => {
createComponent({ event: DESIGN_VERSION_EVENT.DELETION });
expect(wrapper.element).toMatchSnapshot();
});
expect(wrapper.element).toMatchSnapshot();
});
it('renders item with correct status icon for creation event', () => {
createComponent(0, 'CREATION');
it('renders item with correct status icon for creation event', () => {
createComponent({ event: DESIGN_VERSION_EVENT.CREATION });
expect(wrapper.element).toMatchSnapshot();
});
expect(wrapper.element).toMatchSnapshot();
});
it('renders item with no status icon for none event', () => {
createComponent(0, 'NONE');
it('renders loading spinner when isLoading is true', () => {
createComponent({ isLoading: true });
expect(wrapper.element).toMatchSnapshot();
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