Commit 41276da1 authored by Scott Stern's avatar Scott Stern Committed by Illya Klymov

Add assignees to boards sidebar

parent e3ed74b5
...@@ -84,6 +84,10 @@ export default () => { ...@@ -84,6 +84,10 @@ export default () => {
}, },
store, store,
apolloProvider, apolloProvider,
provide: {
// TODO: Mv all non-reactive props from data/props to here.
rootPath: $boardApp.dataset.rootPath,
},
data() { data() {
return { return {
state: boardsStore.state, state: boardsStore.state,
......
...@@ -59,7 +59,7 @@ export default { ...@@ -59,7 +59,7 @@ export default {
}; };
}, },
assigneeUrl() { assigneeUrl() {
return this.user.web_url; return this.user.web_url || this.user.webUrl;
}, },
}, },
}; };
......
<script>
import { n__ } from '~/locale';
import UncollapsedAssigneeList from '~/sidebar/components/assignees/uncollapsed_assignee_list.vue';
export default {
components: {
UncollapsedAssigneeList,
},
inject: ['rootPath'],
props: {
users: {
type: Array,
required: true,
},
},
computed: {
assigneesText() {
return n__('Assignee', '%d Assignees', this.users.length);
},
emptyUsers() {
return this.users.length === 0;
},
},
};
</script>
<template>
<div class="gl-display-flex gl-flex-direction-column">
<label data-testid="assigneeLabel">{{ assigneesText }}</label>
<div v-if="emptyUsers" data-testid="none">
<span>
{{ __('None') }}
</span>
</div>
<uncollapsed-assignee-list v-else :users="users" :root-path="rootPath" />
</div>
</template>
...@@ -73,9 +73,9 @@ export default { ...@@ -73,9 +73,9 @@ export default {
:root-path="rootPath" :root-path="rootPath"
:issuable-type="issuableType" :issuable-type="issuableType"
> >
<div class="ml-2"> <div class="ml-2 gl-line-height-normal">
<span class="author"> {{ user.name }} </span> <div>{{ user.name }}</div>
<span class="username"> {{ username }} </span> <div>{{ username }}</div>
</div> </div>
</assignee-avatar-link> </assignee-avatar-link>
<div v-else> <div v-else>
......
...@@ -361,13 +361,6 @@ ...@@ -361,13 +361,6 @@
margin: 0; margin: 0;
} }
.username {
display: block;
margin-top: 4px;
font-size: 13px;
font-weight: $gl-font-weight-normal;
}
.hide-expanded { .hide-expanded {
display: none; display: none;
} }
......
...@@ -3,10 +3,12 @@ import { mapState, mapActions, mapGetters } from 'vuex'; ...@@ -3,10 +3,12 @@ import { mapState, mapActions, mapGetters } from 'vuex';
import { GlDrawer } from '@gitlab/ui'; import { GlDrawer } from '@gitlab/ui';
import { ISSUABLE } from '~/boards/constants'; import { ISSUABLE } from '~/boards/constants';
import { contentTop } from '~/lib/utils/common_utils'; import { contentTop } from '~/lib/utils/common_utils';
import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
export default { export default {
headerHeight: `${contentTop()}px`, headerHeight: `${contentTop()}px`,
components: { components: {
IssuableAssignees,
GlDrawer, GlDrawer,
}, },
computed: { computed: {
...@@ -38,5 +40,9 @@ export default { ...@@ -38,5 +40,9 @@ export default {
<p class="gl-mb-0">{{ getActiveIssue.referencePath }}</p> <p class="gl-mb-0">{{ getActiveIssue.referencePath }}</p>
</div> </div>
</template> </template>
<template>
<issuable-assignees :users="getActiveIssue.assignees" />
</template>
</gl-drawer> </gl-drawer>
</template> </template>
...@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils'; ...@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
import { GlDrawer } from '@gitlab/ui'; import { GlDrawer } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import BoardContentSidebar from 'ee_component/boards/components/board_content_sidebar.vue'; import BoardContentSidebar from 'ee_component/boards/components/board_content_sidebar.vue';
import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
import { createStore } from '~/boards/stores'; import { createStore } from '~/boards/stores';
import { ISSUABLE } from '~/boards/constants'; import { ISSUABLE } from '~/boards/constants';
...@@ -11,6 +12,9 @@ describe('ee/BoardContentSidebar', () => { ...@@ -11,6 +12,9 @@ describe('ee/BoardContentSidebar', () => {
const createComponent = () => { const createComponent = () => {
wrapper = mount(BoardContentSidebar, { wrapper = mount(BoardContentSidebar, {
provide: {
rootPath: '',
},
store, store,
}); });
}; };
...@@ -19,7 +23,7 @@ describe('ee/BoardContentSidebar', () => { ...@@ -19,7 +23,7 @@ describe('ee/BoardContentSidebar', () => {
store = createStore(); store = createStore();
store.state.sidebarType = ISSUABLE; store.state.sidebarType = ISSUABLE;
store.state.activeId = 1; store.state.activeId = 1;
store.state.issues = { '1': { title: 'One', referencePath: 'path' } }; store.state.issues = { '1': { title: 'One', referencePath: 'path', assignees: [] } };
store.state.activeId = '1'; store.state.activeId = '1';
createComponent(); createComponent();
...@@ -45,6 +49,10 @@ describe('ee/BoardContentSidebar', () => { ...@@ -45,6 +49,10 @@ describe('ee/BoardContentSidebar', () => {
expect(wrapper.find('[data-testid="issue-title"]').text()).toContain('path'); expect(wrapper.find('[data-testid="issue-title"]').text()).toContain('path');
}); });
it('renders IssuableAssignees', () => {
expect(wrapper.find(IssuableAssignees).exists()).toBe(true);
});
describe('when we emit close', () => { describe('when we emit close', () => {
it('hides GlDrawer', async () => { it('hides GlDrawer', async () => {
expect(wrapper.find(GlDrawer).props('open')).toBe(true); expect(wrapper.find(GlDrawer).props('open')).toBe(true);
......
...@@ -152,9 +152,7 @@ RSpec.describe "Issues > User edits issue", :js do ...@@ -152,9 +152,7 @@ RSpec.describe "Issues > User edits issue", :js do
visit project_issue_path(project, issue2) visit project_issue_path(project, issue2)
page.within '.assignee' do page.within '.assignee' do
page.within '.value .author' do expect(page).to have_content user.name
expect(page).to have_content user.name
end
click_link 'Edit' click_link 'Edit'
click_link user.name click_link user.name
......
import { shallowMount } from '@vue/test-utils';
import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
import UncollapsedAssigneeList from '~/sidebar/components/assignees/uncollapsed_assignee_list.vue';
describe('IssuableAssignees', () => {
let wrapper;
const createComponent = (props = { users: [] }) => {
wrapper = shallowMount(IssuableAssignees, {
provide: {
rootPath: '',
},
propsData: { ...props },
});
};
const findLabel = () => wrapper.find('[data-testid="assigneeLabel"');
const findUncollapsedAssigneeList = () => wrapper.find(UncollapsedAssigneeList);
const findEmptyAssignee = () => wrapper.find('[data-testid="none"]');
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('when no assignees are present', () => {
beforeEach(() => {
createComponent();
});
it('renders "None"', () => {
expect(findEmptyAssignee().text()).toBe('None');
});
it('renders "0 assignees"', () => {
expect(findLabel().text()).toBe('0 Assignees');
});
});
describe('when assignees are present', () => {
it('renders UncollapsedAssigneesList', () => {
createComponent({ users: [{ id: 1 }] });
expect(findUncollapsedAssigneeList().exists()).toBe(true);
});
it.each`
assignees | expected
${[{ id: 1 }]} | ${'Assignee'}
${[{ id: 1 }, { id: 2 }]} | ${'2 Assignees'}
`(
'when assignees have a length of $assignees.length, it renders $expected',
({ assignees, expected }) => {
createComponent({ users: assignees });
expect(findLabel().text()).toBe(expected);
},
);
});
});
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