Commit 454f3b81 authored by Florie Guibert's avatar Florie Guibert

Lock a newly created item card in boards

Add loading state for card when creating issue in board
parent 1a7f813b
......@@ -40,6 +40,12 @@ export default {
this.selectedBoardItems.findIndex((boardItem) => boardItem.id === this.item.id) > -1
);
},
isDisabled() {
return this.disabled || !this.item.id || this.item.isLoading;
},
isDraggable() {
return !this.disabled && this.item.id && !this.item.isLoading;
},
},
methods: {
...mapActions(['toggleBoardItemMultiSelection', 'toggleBoardItem']),
......@@ -63,9 +69,10 @@ export default {
data-qa-selector="board_card"
:class="{
'multi-select': multiSelectVisible,
'user-can-drag': !disabled && item.id,
'is-disabled': disabled || !item.id,
'user-can-drag': isDraggable,
'is-disabled': isDisabled,
'is-active': isActive,
'gl-cursor-not-allowed gl-bg-gray-10': item.isLoading,
}"
:index="index"
:data-item-id="item.id"
......
<script>
import { GlLabel, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import { GlLabel, GlTooltipDirective, GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { sortBy } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner';
......@@ -17,6 +17,7 @@ import IssueTimeEstimate from './issue_time_estimate.vue';
export default {
components: {
GlLabel,
GlLoadingIcon,
GlIcon,
UserAvatarLink,
TooltipOnTruncate,
......@@ -181,9 +182,13 @@ export default {
class="confidential-icon gl-mr-2"
:aria-label="__('Confidential')"
/>
<a :href="item.path || item.webUrl || ''" :title="item.title" @mousemove.stop>{{
item.title
}}</a>
<a
:href="item.path || item.webUrl || ''"
:title="item.title"
:class="{ 'gl-text-gray-400': item.isLoading }"
@mousemove.stop
>{{ item.title }}</a
>
</h4>
</div>
<div v-if="showLabelFooter" class="board-card-labels gl-mt-2 gl-display-flex gl-flex-wrap">
......@@ -206,6 +211,7 @@ export default {
<div
class="gl-display-flex align-items-start flex-wrap-reverse board-card-number-container gl-overflow-hidden js-board-card-number-container"
>
<gl-loading-icon v-if="item.isLoading" size="md" class="mt-3" />
<span
v-if="item.referencePath"
class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3"
......
......@@ -509,7 +509,7 @@ export default {
input.projectPath = fullPath;
}
const placeholderIssue = formatIssue({ ...issueInput, id: placeholderId });
const placeholderIssue = formatIssue({ ...issueInput, id: placeholderId, isLoading: true });
dispatch('addListItem', { list, item: placeholderIssue, position: 0 });
gqlClient
......
---
title: Lock a newly created item card in boards
merge_request: 61958
author:
type: changed
import { GlLabel } from '@gitlab/ui';
import { GlLabel, GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { range } from 'lodash';
import Vuex from 'vuex';
......@@ -63,6 +63,7 @@ describe('Board card component', () => {
},
stubs: {
GlLabel: true,
GlLoadingIcon: true,
},
mocks: {
$apollo: {
......@@ -121,6 +122,10 @@ describe('Board card component', () => {
expect(wrapper.find('.board-card-assignee .avatar').exists()).toBe(false);
});
it('does not render loading icon', () => {
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
});
describe('blocked', () => {
it('renders blocked icon if issue is blocked', async () => {
createWrapper({
......@@ -399,4 +404,17 @@ describe('Board card component', () => {
});
});
});
describe('loading', () => {
it('renders loading icon', async () => {
createWrapper({
item: {
...issue,
isLoading: true,
},
});
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
});
import { GlLabel } from '@gitlab/ui';
import { createLocalVue, shallowMount, mount } from '@vue/test-utils';
import { shallowMount, mount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import BoardCard from '~/boards/components/board_card.vue';
......@@ -12,8 +13,7 @@ describe('Board card', () => {
let store;
let mockActions;
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.use(Vuex);
const createStore = ({ initialState = {} } = {}) => {
mockActions = {
......@@ -41,14 +41,14 @@ describe('Board card', () => {
provide = {},
mountFn = shallowMount,
stubs = { BoardCardInner },
item = mockIssue,
} = {}) => {
wrapper = mountFn(BoardCard, {
localVue,
stubs,
store,
propsData: {
list: mockLabelList,
item: mockIssue,
item,
disabled: false,
index: 0,
...propsData,
......@@ -151,4 +151,24 @@ describe('Board card', () => {
});
});
});
describe('when card is loading', () => {
it('card is disabled and user cannot drag', () => {
createStore();
mountComponent({ item: { ...mockIssue, isLoading: true } });
expect(wrapper.classes()).toContain('is-disabled');
expect(wrapper.classes()).not.toContain('user-can-drag');
});
});
describe('when card is not loading', () => {
it('user can drag', () => {
createStore();
mountComponent();
expect(wrapper.classes()).not.toContain('is-disabled');
expect(wrapper.classes()).toContain('user-can-drag');
});
});
});
......@@ -1244,7 +1244,7 @@ describe('addListNewIssue', () => {
type: 'addListItem',
payload: {
list: fakeList,
item: formatIssue({ ...mockIssue, id: 'tmp' }),
item: formatIssue({ ...mockIssue, id: 'tmp', isLoading: true }),
position: 0,
},
},
......@@ -1286,7 +1286,7 @@ describe('addListNewIssue', () => {
type: 'addListItem',
payload: {
list: fakeList,
item: formatIssue({ ...mockIssue, id: 'tmp' }),
item: formatIssue({ ...mockIssue, id: 'tmp', isLoading: true }),
position: 0,
},
},
......
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