Commit bcbb31f6 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch '229843-move-slot-content-into-related-issues-component' into 'master'

Move slot content from RelatedIssuesList to RelatedIssuableItem

See merge request gitlab-org/gitlab!38181
parents 932d5efc 191b422c
...@@ -4,6 +4,7 @@ import { GlIcon, GlTooltip, GlTooltipDirective } from '@gitlab/ui'; ...@@ -4,6 +4,7 @@ import { GlIcon, GlTooltip, GlTooltipDirective } from '@gitlab/ui';
import { sprintf } from '~/locale'; import { sprintf } from '~/locale';
import IssueMilestone from './issue_milestone.vue'; import IssueMilestone from './issue_milestone.vue';
import IssueAssignees from './issue_assignees.vue'; import IssueAssignees from './issue_assignees.vue';
import IssueDueDate from '~/boards/components/issue_due_date.vue';
import relatedIssuableMixin from '../../mixins/related_issuable_mixin'; import relatedIssuableMixin from '../../mixins/related_issuable_mixin';
import CiIcon from '../ci_icon.vue'; import CiIcon from '../ci_icon.vue';
...@@ -15,6 +16,8 @@ export default { ...@@ -15,6 +16,8 @@ export default {
CiIcon, CiIcon,
GlIcon, GlIcon,
GlTooltip, GlTooltip,
IssueWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
IssueDueDate,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -120,8 +123,21 @@ export default { ...@@ -120,8 +123,21 @@ export default {
/> />
<!-- Flex order for slots is defined in the parent component: e.g. related_issues_block.vue --> <!-- Flex order for slots is defined in the parent component: e.g. related_issues_block.vue -->
<slot name="dueDate"></slot> <span v-if="weight > 0" class="order-md-1">
<slot name="weight"></slot> <issue-weight
:weight="weight"
class="item-weight gl-display-flex gl-align-items-center"
tag-name="span"
/>
</span>
<span v-if="dueDate" class="order-md-1">
<issue-due-date
:date="dueDate"
tooltip-placement="top"
css-class="item-due-date gl-display-flex gl-align-items-center"
/>
</span>
<issue-assignees <issue-assignees
v-if="hasAssignees" v-if="hasAssignees"
......
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import IssueWeight from 'ee/boards/components/issue_card_weight.vue';
import sortableConfig from 'ee/sortable/sortable_config'; import sortableConfig from 'ee/sortable/sortable_config';
import IssueDueDate from '~/boards/components/issue_due_date.vue';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue'; import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
...@@ -14,8 +12,6 @@ export default { ...@@ -14,8 +12,6 @@ export default {
}, },
components: { components: {
GlLoadingIcon, GlLoadingIcon,
IssueDueDate,
IssueWeight,
RelatedIssuableItem, RelatedIssuableItem,
}, },
props: { props: {
...@@ -132,29 +128,15 @@ export default { ...@@ -132,29 +128,15 @@ export default {
:assignees="issue.assignees" :assignees="issue.assignees"
:created-at="issue.createdAt" :created-at="issue.createdAt"
:closed-at="issue.closedAt" :closed-at="issue.closedAt"
:weight="issue.weight"
:due-date="issue.dueDate"
:can-remove="canAdmin" :can-remove="canAdmin"
:can-reorder="canReorder" :can-reorder="canReorder"
:path-id-separator="pathIdSeparator" :path-id-separator="pathIdSeparator"
event-namespace="relatedIssue" event-namespace="relatedIssue"
class="qa-related-issuable-item" class="qa-related-issuable-item"
@relatedIssueRemoveRequest="$emit('relatedIssueRemoveRequest', $event)" @relatedIssueRemoveRequest="$emit('relatedIssueRemoveRequest', $event)"
> />
<span v-if="issue.weight > 0" slot="weight" class="order-md-1">
<issue-weight
:weight="issue.weight"
class="item-weight d-flex align-items-center"
tag-name="span"
/>
</span>
<span v-if="issue.dueDate" slot="dueDate" class="order-md-1">
<issue-due-date
:date="issue.dueDate"
tooltip-placement="top"
css-class="item-due-date d-flex align-items-center"
/>
</span>
</related-issuable-item>
</li> </li>
</ul> </ul>
</div> </div>
......
import { isAbsolute, isSafeURL } from '~/lib/utils/url_utility'; import { isAbsolute, isSafeURL } from '~/lib/utils/url_utility';
import { REGEXES } from './constants'; import { REGEXES } from './constants';
window.isAbsolute = isAbsolute;
window.isSafeURL = isSafeURL;
// Get the issue in the format expected by the descendant components of related_issues_block.vue. // Get the issue in the format expected by the descendant components of related_issues_block.vue.
export const getFormattedIssue = issue => ({ export const getFormattedIssue = issue => ({
...issue, ...issue,
......
import { mount } from '@vue/test-utils';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
import IssueWeight from 'ee_component/boards/components/issue_card_weight.vue';
import {
defaultAssignees,
defaultMilestone,
} from 'jest/vue_shared/components/issue/related_issuable_mock_data';
import { TEST_HOST } from 'jest/helpers/test_constants';
describe('RelatedIssuableItem', () => {
let wrapper;
function mountComponent({ mountMethod = mount, stubs = {}, props = {}, slots = {} } = {}) {
wrapper = mountMethod(RelatedIssuableItem, {
propsData: props,
slots,
stubs,
});
}
const props = {
idKey: 1,
displayReference: 'gitlab-org/gitlab-test#1',
pathIdSeparator: '#',
path: `${TEST_HOST}/path`,
title: 'title',
confidential: true,
dueDate: '1990-12-31',
weight: 10,
createdAt: '2018-12-01T00:00:00.00Z',
milestone: defaultMilestone,
assignees: defaultAssignees,
eventNamespace: 'relatedIssue',
};
const slots = {
dueDate: '<div class="js-due-date-slot"></div>',
weight: '<div class="js-weight-slot"></div>',
};
beforeEach(() => {
mountComponent({ props, slots });
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders weight component with correct weight', () => {
expect(wrapper.find(IssueWeight).props('weight')).toBe(props.weight);
});
});
import Vue from 'vue';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue'; import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
import IssueDueDate from '~/boards/components/issue_due_date.vue';
import { defaultAssignees, defaultMilestone } from './related_issuable_mock_data'; import { defaultAssignees, defaultMilestone } from './related_issuable_mock_data';
import { TEST_HOST } from 'jest/helpers/test_constants'; import { TEST_HOST } from 'jest/helpers/test_constants';
...@@ -71,85 +71,65 @@ describe('RelatedIssuableItem', () => { ...@@ -71,85 +71,65 @@ describe('RelatedIssuableItem', () => {
}); });
describe('token state', () => { describe('token state', () => {
let tokenState; const tokenState = () => wrapper.find({ ref: 'iconElementXL' });
beforeEach(done => { beforeEach(() => {
wrapper.setProps({ state: 'opened' }); wrapper.setProps({ state: 'opened' });
Vue.nextTick(() => {
tokenState = wrapper.find('.issue-token-state-icon-open');
done();
});
}); });
it('renders if hasState', () => { it('renders if hasState', () => {
expect(tokenState.exists()).toBe(true); expect(tokenState().exists()).toBe(true);
}); });
it('renders state title', () => { it('renders state title', () => {
const stateTitle = tokenState.attributes('title'); const stateTitle = tokenState().attributes('title');
const formattedCreateDate = formatDate(props.createdAt); const formattedCreateDate = formatDate(props.createdAt);
expect(stateTitle).toContain('<span class="bold">Opened</span>'); expect(stateTitle).toContain('<span class="bold">Opened</span>');
expect(stateTitle).toContain(`<span class="text-tertiary">${formattedCreateDate}</span>`); expect(stateTitle).toContain(`<span class="text-tertiary">${formattedCreateDate}</span>`);
}); });
it('renders aria label', () => { it('renders aria label', () => {
expect(tokenState.attributes('aria-label')).toEqual('opened'); expect(tokenState().attributes('aria-label')).toEqual('opened');
}); });
it('renders open icon when open state', () => { it('renders open icon when open state', () => {
expect(tokenState.classes('issue-token-state-icon-open')).toBe(true); expect(tokenState().classes('issue-token-state-icon-open')).toBe(true);
}); });
it('renders close icon when close state', done => { it('renders close icon when close state', async () => {
wrapper.setProps({ wrapper.setProps({
state: 'closed', state: 'closed',
closedAt: '2018-12-01T00:00:00.00Z', closedAt: '2018-12-01T00:00:00.00Z',
}); });
await wrapper.vm.$nextTick();
Vue.nextTick(() => { expect(tokenState().classes('issue-token-state-icon-closed')).toBe(true);
expect(tokenState.classes('issue-token-state-icon-closed')).toBe(true);
done();
});
}); });
}); });
describe('token metadata', () => { describe('token metadata', () => {
let tokenMetadata; const tokenMetadata = () => wrapper.find('.item-meta');
beforeEach(done => {
Vue.nextTick(() => {
tokenMetadata = wrapper.find('.item-meta');
done();
});
});
it('renders item path and ID', () => { it('renders item path and ID', () => {
const pathAndID = tokenMetadata.find('.item-path-id').text(); const pathAndID = tokenMetadata()
.find('.item-path-id')
.text();
expect(pathAndID).toContain('gitlab-org/gitlab-test'); expect(pathAndID).toContain('gitlab-org/gitlab-test');
expect(pathAndID).toContain('#1'); expect(pathAndID).toContain('#1');
}); });
it('renders milestone icon and name', () => { it('renders milestone icon and name', () => {
const milestoneIcon = tokenMetadata.find('.item-milestone svg use'); const milestoneIcon = tokenMetadata().find('.item-milestone svg use');
const milestoneTitle = tokenMetadata.find('.item-milestone .milestone-title'); const milestoneTitle = tokenMetadata().find('.item-milestone .milestone-title');
expect(milestoneIcon.attributes('href')).toContain('clock'); expect(milestoneIcon.attributes('href')).toContain('clock');
expect(milestoneTitle.text()).toContain('Milestone title'); expect(milestoneTitle.text()).toContain('Milestone title');
}); });
it('renders due date component', () => { it('renders due date component with correct due date', () => {
expect(tokenMetadata.find('.js-due-date-slot').exists()).toBe(true); expect(wrapper.find(IssueDueDate).props('date')).toBe(props.dueDate);
});
it('renders weight component', () => {
expect(tokenMetadata.find('.js-weight-slot').exists()).toBe(true);
}); });
}); });
...@@ -163,40 +143,30 @@ describe('RelatedIssuableItem', () => { ...@@ -163,40 +143,30 @@ describe('RelatedIssuableItem', () => {
}); });
describe('remove button', () => { describe('remove button', () => {
let removeBtn; const removeButton = () => wrapper.find({ ref: 'removeButton' });
beforeEach(done => { beforeEach(() => {
wrapper.setProps({ canRemove: true }); wrapper.setProps({ canRemove: true });
Vue.nextTick(() => {
removeBtn = wrapper.find({ ref: 'removeButton' });
done();
});
}); });
it('renders if canRemove', () => { it('renders if canRemove', () => {
expect(removeBtn.exists()).toBe(true); expect(removeButton().exists()).toBe(true);
}); });
it('renders disabled button when removeDisabled', done => { it('renders disabled button when removeDisabled', async () => {
wrapper.vm.removeDisabled = true; wrapper.setData({ removeDisabled: true });
await wrapper.vm.$nextTick();
Vue.nextTick(() => { expect(removeButton().attributes('disabled')).toEqual('disabled');
expect(removeBtn.attributes('disabled')).toEqual('disabled');
done();
});
}); });
it('triggers onRemoveRequest when clicked', () => { it('triggers onRemoveRequest when clicked', async () => {
removeBtn.trigger('click'); removeButton().trigger('click');
await wrapper.vm.$nextTick();
const { relatedIssueRemoveRequest } = wrapper.emitted();
return wrapper.vm.$nextTick().then(() => { expect(relatedIssueRemoveRequest.length).toBe(1);
const { relatedIssueRemoveRequest } = wrapper.emitted(); expect(relatedIssueRemoveRequest[0]).toEqual([props.idKey]);
expect(relatedIssueRemoveRequest.length).toBe(1);
expect(relatedIssueRemoveRequest[0]).toEqual([props.idKey]);
});
}); });
}); });
}); });
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