Commit d9b24c69 authored by Eulyeon Ko's avatar Eulyeon Ko Committed by Simon Knox

Create CE version of new sidebar component

While epic swimlane was in development,
board_content_sidebar.vue had been placed exclusively under EE.

board_content_sidebar.vue is the new graphql-based sidebar and
we are splitting it into CE and EE versions to prepare for
using it across project and group issue boards.
parent 56139a7a
......@@ -17,7 +17,7 @@ export default {
gon.features?.graphqlBoardLists || gon.features?.epicBoards
? BoardColumn
: BoardColumnDeprecated,
BoardContentSidebar: () => import('ee_component/boards/components/board_content_sidebar.vue'),
BoardContentSidebar: () => import('~/boards/components/board_content_sidebar.vue'),
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
GlAlert,
},
......@@ -134,6 +134,9 @@ export default {
:disabled="disabled"
/>
<board-content-sidebar v-if="isSwimlanesOn || glFeatures.graphqlBoardLists" />
<board-content-sidebar
v-if="isSwimlanesOn || glFeatures.graphqlBoardLists"
class="issue-boards-sidebar"
/>
</div>
</template>
<script>
import { GlDrawer } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex';
import SidebarIterationWidget from 'ee/sidebar/components/sidebar_iteration_widget.vue';
import BoardSidebarEpicSelect from 'ee_component/boards/components/sidebar/board_sidebar_epic_select.vue';
import BoardSidebarWeightInput from 'ee_component/boards/components/sidebar/board_sidebar_weight_input.vue';
import SidebarIterationWidget from 'ee_component/sidebar/components/sidebar_iteration_widget.vue';
import BoardSidebarDueDate from '~/boards/components/sidebar/board_sidebar_due_date.vue';
import BoardSidebarIssueTitle from '~/boards/components/sidebar/board_sidebar_issue_title.vue';
import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue';
import BoardSidebarMilestoneSelect from '~/boards/components/sidebar/board_sidebar_milestone_select.vue';
import BoardSidebarSubscription from '~/boards/components/sidebar/board_sidebar_subscription.vue';
import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue';
import { ISSUABLE } from '~/boards/constants';
import { contentTop } from '~/lib/utils/common_utils';
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import BoardSidebarEpicSelect from './sidebar/board_sidebar_epic_select.vue';
import BoardSidebarTimeTracker from './sidebar/board_sidebar_time_tracker.vue';
import BoardSidebarWeightInput from './sidebar/board_sidebar_weight_input.vue';
export default {
headerHeight: `${contentTop()}px`,
components: {
GlDrawer,
BoardSidebarIssueTitle,
BoardSidebarEpicSelect,
SidebarAssigneesWidget,
BoardSidebarTimeTracker,
BoardSidebarWeightInput,
BoardSidebarLabelsSelect,
BoardSidebarDueDate,
BoardSidebarSubscription,
BoardSidebarMilestoneSelect,
BoardSidebarEpicSelect,
SidebarIterationWidget,
BoardSidebarWeightInput,
},
mixins: [glFeatureFlagsMixin()],
computed: {
......@@ -45,6 +45,9 @@ export default {
showSidebar() {
return this.isIssuableSidebar && this.isSidebarOpen;
},
fullPath() {
return this.activeIssue?.referencePath?.split('#')[0] || '';
},
},
methods: {
...mapActions(['toggleBoardItem', 'setAssignees']),
......@@ -72,11 +75,12 @@ export default {
<board-sidebar-issue-title />
<sidebar-assignees-widget
:iid="activeIssue.iid"
:full-path="activeIssue.referencePath.split('#')[0]"
:full-path="fullPath"
:initial-assignees="activeIssue.assignees"
class="assignee"
@assignees-updated="updateAssignees"
/>
<board-sidebar-epic-select />
<board-sidebar-epic-select class="epic" />
<div>
<board-sidebar-milestone-select />
<sidebar-iteration-widget
......@@ -89,9 +93,9 @@ export default {
</div>
<board-sidebar-time-tracker class="swimlanes-sidebar-time-tracker" />
<board-sidebar-due-date />
<board-sidebar-labels-select />
<board-sidebar-weight-input v-if="glFeatures.issueWeights" />
<board-sidebar-subscription />
<board-sidebar-labels-select class="labels" />
<board-sidebar-weight-input v-if="glFeatures.issueWeights" class="weight" />
<board-sidebar-subscription class="subscriptions" />
</template>
</gl-drawer>
</template>
......@@ -98,14 +98,14 @@ export default {
<gl-button
v-if="canUpdate"
variant="link"
class="gl-text-gray-900! gl-ml-5 js-sidebar-dropdown-toggle"
class="gl-text-gray-900! gl-ml-5 js-sidebar-dropdown-toggle edit-link"
data-testid="edit-button"
@click="toggle"
>
{{ __('Edit') }}
</gl-button>
</header>
<div v-show="!edit" class="gl-text-gray-500" data-testid="collapsed-content">
<div v-show="!edit" class="gl-text-gray-500 value" data-testid="collapsed-content">
<slot name="collapsed">{{ __('None') }}</slot>
</div>
<div v-show="edit" data-testid="expanded-content">
......
......@@ -341,7 +341,7 @@ export default () => {
},
computed: {
disabled() {
if (!this.store) {
if (!this.store || !this.store.lists) {
return true;
}
return !this.store.lists.filter((list) => !list.preset).length;
......
......@@ -29,7 +29,7 @@ export default {
v-on="$listeners"
>
<gl-icon name="weight" class="board-card-info-icon gl-mr-2" />
<span class="board-card-info-text"> {{ weight }} </span>
<span data-testid="board-card-weight" class="board-card-info-text"> {{ weight }} </span>
<gl-tooltip
:target="() => $refs.itemWeight"
placement="bottom"
......
......@@ -74,7 +74,11 @@ export default {
>
<template v-if="hasWeight" #collapsed>
<div class="gl-display-flex gl-align-items-center">
<strong class="gl-text-gray-900">{{ activeIssue.weight }}</strong>
<strong
class="gl-text-gray-900 js-weight-weight-label-value"
data-qa-selector="weight_label_value"
>{{ activeIssue.weight }}</strong
>
<span class="gl-mx-2">-</span>
<gl-button
variant="link"
......
import { GlDrawer } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import BoardContentSidebar from 'ee_component/boards/components/board_content_sidebar.vue';
import SidebarIterationWidget from 'ee_component/sidebar/components/sidebar_iteration_widget';
import { stubComponent } from 'helpers/stub_component';
import BoardSidebarDueDate from '~/boards/components/sidebar/board_sidebar_due_date.vue';
import BoardSidebarIssueTitle from '~/boards/components/sidebar/board_sidebar_issue_title.vue';
import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue';
import BoardSidebarMilestoneSelect from '~/boards/components/sidebar/board_sidebar_milestone_select.vue';
import BoardSidebarSubscription from '~/boards/components/sidebar/board_sidebar_subscription.vue';
import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue';
import { ISSUABLE, issuableTypes } from '~/boards/constants';
import { mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data';
......@@ -70,62 +65,7 @@ describe('ee/BoardContentSidebar', () => {
wrapper = null;
});
it('confirms we render GlDrawer', () => {
expect(wrapper.find(GlDrawer).exists()).toBe(true);
});
it('does not render GlDrawer when isSidebarOpen is false', () => {
createStore({ mockGetters: { isSidebarOpen: () => false } });
createComponent();
expect(wrapper.find(GlDrawer).exists()).toBe(false);
});
it('applies an open attribute', () => {
expect(wrapper.find(GlDrawer).props('open')).toBe(true);
});
it('renders BoardSidebarLabelsSelect', () => {
expect(wrapper.find(BoardSidebarLabelsSelect).exists()).toBe(true);
});
it('renders BoardSidebarIssueTitle', () => {
expect(wrapper.find(BoardSidebarIssueTitle).exists()).toBe(true);
});
it('renders BoardSidebarDueDate', () => {
expect(wrapper.find(BoardSidebarDueDate).exists()).toBe(true);
});
it('renders BoardSidebarSubscription', () => {
expect(wrapper.find(BoardSidebarSubscription).exists()).toBe(true);
});
it('renders BoardSidebarMilestoneSelect', () => {
expect(wrapper.find(BoardSidebarMilestoneSelect).exists()).toBe(true);
});
it('renders SidebarIterationWidget', () => {
expect(wrapper.find(SidebarIterationWidget).exists()).toBe(true);
});
describe('when we emit close', () => {
let toggleBoardItem;
beforeEach(() => {
toggleBoardItem = jest.fn();
createStore({ mockActions: { toggleBoardItem } });
createComponent();
});
it('calls toggleBoardItem with correct parameters', async () => {
wrapper.find(GlDrawer).vm.$emit('close');
expect(toggleBoardItem).toHaveBeenCalledTimes(1);
expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), {
boardItem: mockIssue,
sidebarType: ISSUABLE,
});
});
});
});
import { shallowMount } from '@vue/test-utils';
import BoardContentSidebar from 'ee/boards/components/board_content_sidebar.vue';
import BoardContent from '~/boards/components/board_content.vue';
import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue';
import { createStore } from '~/boards/stores';
describe('ee/BoardContent', () => {
......
import { GlDrawer } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { stubComponent } from 'helpers/stub_component';
import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue';
import BoardSidebarDueDate from '~/boards/components/sidebar/board_sidebar_due_date.vue';
import BoardSidebarIssueTitle from '~/boards/components/sidebar/board_sidebar_issue_title.vue';
import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue';
import BoardSidebarMilestoneSelect from '~/boards/components/sidebar/board_sidebar_milestone_select.vue';
import BoardSidebarSubscription from '~/boards/components/sidebar/board_sidebar_subscription.vue';
import { ISSUABLE } from '~/boards/constants';
import { mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data';
describe('BoardContentSidebar', () => {
let wrapper;
let store;
const createStore = ({ mockGetters = {}, mockActions = {} } = {}) => {
store = new Vuex.Store({
state: {
sidebarType: ISSUABLE,
issues: { [mockIssue.id]: mockIssue },
activeId: mockIssue.id,
issuableType: 'issue',
},
getters: {
activeIssue: () => mockIssue,
groupPathForActiveIssue: () => mockIssueGroupPath,
projectPathForActiveIssue: () => mockIssueProjectPath,
isSidebarOpen: () => true,
...mockGetters,
},
actions: mockActions,
});
};
const createComponent = () => {
wrapper = shallowMount(BoardContentSidebar, {
provide: {
canUpdate: true,
rootPath: '/',
groupId: '#',
},
store,
stubs: {
GlDrawer: stubComponent(GlDrawer, {
template: '<div><slot name="header"></slot><slot></slot></div>',
}),
},
mocks: {
$apollo: {
queries: {
participants: {
loading: false,
},
},
},
},
});
};
beforeEach(() => {
createStore();
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('confirms we render GlDrawer', () => {
expect(wrapper.find(GlDrawer).exists()).toBe(true);
});
it('does not render GlDrawer when isSidebarOpen is false', () => {
createStore({ mockGetters: { isSidebarOpen: () => false } });
createComponent();
expect(wrapper.find(GlDrawer).exists()).toBe(false);
});
it('applies an open attribute', () => {
expect(wrapper.find(GlDrawer).props('open')).toBe(true);
});
it('renders BoardSidebarLabelsSelect', () => {
expect(wrapper.find(BoardSidebarLabelsSelect).exists()).toBe(true);
});
it('renders BoardSidebarIssueTitle', () => {
expect(wrapper.find(BoardSidebarIssueTitle).exists()).toBe(true);
});
it('renders BoardSidebarDueDate', () => {
expect(wrapper.find(BoardSidebarDueDate).exists()).toBe(true);
});
it('renders BoardSidebarSubscription', () => {
expect(wrapper.find(BoardSidebarSubscription).exists()).toBe(true);
});
it('renders BoardSidebarMilestoneSelect', () => {
expect(wrapper.find(BoardSidebarMilestoneSelect).exists()).toBe(true);
});
describe('when we emit close', () => {
let toggleBoardItem;
beforeEach(() => {
toggleBoardItem = jest.fn();
createStore({ mockActions: { toggleBoardItem } });
createComponent();
});
it('calls toggleBoardItem with correct parameters', async () => {
wrapper.find(GlDrawer).vm.$emit('close');
expect(toggleBoardItem).toHaveBeenCalledTimes(1);
expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), {
boardItem: mockIssue,
sidebarType: ISSUABLE,
});
});
});
});
......@@ -7,7 +7,7 @@
*/
import { shallowMount } from '@vue/test-utils';
import BoardSidebarTimeTracker from 'ee/boards/components/sidebar/board_sidebar_time_tracker.vue';
import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue';
import { createStore } from '~/boards/stores';
import IssuableTimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
......
......@@ -125,7 +125,7 @@ export const labels = [
export const rawIssue = {
title: 'Issue 1',
id: 'gid://gitlab/Issue/436',
iid: 27,
iid: '27',
dueDate: null,
timeEstimate: 0,
weight: null,
......@@ -152,7 +152,7 @@ export const rawIssue = {
export const mockIssue = {
id: 'gid://gitlab/Issue/436',
iid: 27,
iid: '27',
title: 'Issue 1',
dueDate: null,
timeEstimate: 0,
......@@ -399,6 +399,9 @@ export const mockActiveGroupProjects = [
{ ...mockGroupProject2, archived: false },
];
export const mockIssueGroupPath = 'gitlab-org';
export const mockIssueProjectPath = `${mockIssueGroupPath}/gitlab-test`;
export const mockBlockingIssue1 = {
id: 'gid://gitlab/Issue/525',
iid: '6',
......
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