Commit 02fc39a4 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '326963-lock-a-newly-created-item-card-in-boards' into 'master'

Lock a newly created item card in boards

See merge request gitlab-org/gitlab!61958
parents a1baedce ef50166f
......@@ -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"
......
......@@ -86,7 +86,9 @@ export default {
: this.$options.i18n.showingAllIssues;
},
treeRootWrapper() {
return this.canAdminList ? Draggable : 'ul';
return this.canAdminList && !this.listsFlags[this.list.id]?.addItemToListInProgress
? Draggable
: 'ul';
},
treeRootOptions() {
const options = {
......
......@@ -102,7 +102,7 @@ export default {
ref="submitButton"
:disabled="disabled"
class="float-left js-no-auto-disable"
variant="success"
variant="confirm"
category="primary"
type="submit"
>
......
......@@ -489,8 +489,13 @@ export default {
});
},
addListItem: ({ commit }, { list, item, position }) => {
commit(types.ADD_BOARD_ITEM_TO_LIST, { listId: list.id, itemId: item.id, atIndex: position });
addListItem: ({ commit }, { list, item, position, inProgress = false }) => {
commit(types.ADD_BOARD_ITEM_TO_LIST, {
listId: list.id,
itemId: item.id,
atIndex: position,
inProgress,
});
commit(types.UPDATE_BOARD_ITEM, item);
},
......@@ -509,8 +514,8 @@ export default {
input.projectPath = fullPath;
}
const placeholderIssue = formatIssue({ ...issueInput, id: placeholderId });
dispatch('addListItem', { list, item: placeholderIssue, position: 0 });
const placeholderIssue = formatIssue({ ...issueInput, id: placeholderId, isLoading: true });
dispatch('addListItem', { list, item: placeholderIssue, position: 0, inProgress: true });
gqlClient
.mutate({
......
......@@ -166,8 +166,9 @@ export default {
[mutationTypes.ADD_BOARD_ITEM_TO_LIST]: (
state,
{ itemId, listId, moveBeforeId, moveAfterId, atIndex },
{ itemId, listId, moveBeforeId, moveAfterId, atIndex, inProgress = false },
) => {
Vue.set(state.listsFlags, listId, { ...state.listsFlags, addItemToListInProgress: inProgress });
addItemToList({ state, listId, itemId, moveBeforeId, moveAfterId, atIndex });
},
......
---
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');
});
});
});
......@@ -1114,6 +1114,7 @@ describe('addListItem', () => {
listId: mockLists[0].id,
itemId: mockIssue.id,
atIndex: 0,
inProgress: false,
},
},
{ type: types.UPDATE_BOARD_ITEM, payload: mockIssue },
......@@ -1244,8 +1245,9 @@ describe('addListNewIssue', () => {
type: 'addListItem',
payload: {
list: fakeList,
item: formatIssue({ ...mockIssue, id: 'tmp' }),
item: formatIssue({ ...mockIssue, id: 'tmp', isLoading: true }),
position: 0,
inProgress: true,
},
},
{ type: 'removeListItem', payload: { listId: fakeList.id, itemId: 'tmp' } },
......@@ -1286,8 +1288,9 @@ describe('addListNewIssue', () => {
type: 'addListItem',
payload: {
list: fakeList,
item: formatIssue({ ...mockIssue, id: 'tmp' }),
item: formatIssue({ ...mockIssue, id: 'tmp', isLoading: true }),
position: 0,
inProgress: true,
},
},
{ type: 'removeListItem', payload: { listId: fakeList.id, itemId: 'tmp' } },
......
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