Commit afb7260e authored by Fatih Acet's avatar Fatih Acet

Merge branch 'winh-issue-board-switcher-vue-ee' into 'master'

Refactor issue boards switcher to single file Vue component

See merge request gitlab-org/gitlab-ee!8519
parents 5ec9214d a308ab28
......@@ -27,7 +27,7 @@ import 'ee/boards/models/list';
import 'ee/boards/models/issue';
import 'ee/boards/models/project';
import BoardService from 'ee/boards/services/board_service';
import BoardsSelector from 'ee/boards/components/boards_selector';
import BoardsSelector from 'ee/boards/components/boards_selector.vue';
import collapseIcon from 'ee/boards/icons/fullscreen_collapse.svg';
import expandIcon from 'ee/boards/icons/fullscreen_expand.svg';
import tooltip from '~/vue_shared/directives/tooltip';
......@@ -356,11 +356,34 @@ export default () => {
`,
});
const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher');
// eslint-disable-next-line no-new
new Vue({
el: '#js-multiple-boards-switcher',
el: boardsSwitcherElement,
components: {
BoardsSelector,
},
data() {
const { dataset } = boardsSwitcherElement;
const boardsSelectorProps = {
...dataset,
currentBoard: JSON.parse(dataset.currentBoard),
hasMissingBoards: dataset.hasMissingBoards === 'true',
canAdminBoard: dataset.canAdminBoard === 'true',
multipleIssueBoardsAvailable: dataset.multipleIssueBoardsAvailable === 'true',
projectId: Number(dataset.projectId),
groupId: Number(dataset.groupId),
scopedIssueBoardFeatureEnabled: dataset.scopedIssueBoardFeatureEnabled === 'true',
weights: JSON.parse(dataset.weights),
};
return { boardsSelectorProps };
},
render(createElement) {
return createElement(BoardsSelector, {
props: this.boardsSelectorProps,
});
},
});
};
......@@ -7,8 +7,7 @@
.issues-filters
.issues-details-filters.filtered-search-block{ class: block_css_class, "v-pre" => type == :boards_modal }
- if type == :boards
#js-multiple-boards-switcher.inline.boards-switcher{ "v-cloak" => true }
= render_if_exists "shared/boards/switcher", board: board
= render_if_exists "shared/boards/switcher", board: board
= form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form' do
- if params[:search].present?
= hidden_field_tag :search, params[:search]
......
......@@ -57,9 +57,9 @@ export default {
default: 0,
},
weights: {
type: String,
type: Array,
required: false,
default: '',
default: () => [],
},
},
data() {
......@@ -126,9 +126,6 @@ export default {
readonly() {
return !this.canAdminBoard;
},
weightsArray() {
return JSON.parse(this.weights);
},
submitDisabled() {
return this.isLoading || this.board.name.length === 0;
},
......@@ -269,7 +266,7 @@ export default {
<board-weight-select
v-model="board.weight"
:board="board"
:weights="weightsArray"
:weights="weights"
:can-edit="canAdminBoard"
/>
</div>
......
import Vue from 'vue';
<script>
import $ from 'jquery';
import { throttle } from 'underscore';
import { GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import boardsStore from '~/boards/stores/boards_store';
import BoardForm from './board_form.vue';
import AssigneeList from './assignees_list_slector';
import MilestoneList from './milestone_list_selector';
export default Vue.extend({
export default {
name: 'BoardsSelector',
components: {
Icon,
BoardForm,
GlLoadingIcon,
},
props: {
currentBoard: {
......@@ -24,6 +28,42 @@ export default Vue.extend({
type: Number,
default: 200,
},
boardBaseUrl: {
type: String,
required: true,
},
hasMissingBoards: {
type: Boolean,
required: true,
},
canAdminBoard: {
type: Boolean,
required: true,
},
multipleIssueBoardsAvailable: {
type: Boolean,
required: true,
},
labelsPath: {
type: String,
required: true,
},
projectId: {
type: Number,
required: true,
},
groupId: {
type: Number,
required: true,
},
scopedIssueBoardFeatureEnabled: {
type: Boolean,
required: true,
},
weights: {
type: Array,
required: true,
},
},
data() {
return {
......@@ -151,4 +191,75 @@ export default Vue.extend({
}
},
},
});
};
</script>
<template>
<div class="boards-switcher js-boards-selector">
<span class="boards-selector-wrapper js-boards-selector-wrapper">
<div class="dropdown">
<button
class="dropdown-menu-toggle js-dropdown-toggle"
type="button"
data-toggle="dropdown"
@click="loadBoards"
>
{{ board.name }} <icon name="chevron-down" />
</button>
<div class="dropdown-menu" :class="{ 'is-loading': loading }">
<div class="dropdown-content-faded-mask js-scroll-fade" :class="scrollFadeClass">
<ul
v-if="!loading"
ref="content"
class="dropdown-list js-dropdown-list"
@scroll.passive="throttledSetScrollFade"
>
<li
v-for="otherBoard in boards"
:key="otherBoard.id"
class="dropdown-item js-dropdown-item"
>
<a :href="`${boardBaseUrl}/${otherBoard.id}`"> {{ otherBoard.name }} </a>
</li>
<li v-if="hasMissingBoards" class="small unclickable">
{{
s__(
'IssueBoards|Some of your boards are hidden, activate a license to see them again.',
)
}}
</li>
</ul>
</div>
<gl-loading-icon v-if="loading" class="dropdown-loading" />
<div v-if="canAdminBoard" class="dropdown-footer">
<ul class="dropdown-footer-list">
<li v-if="multipleIssueBoardsAvailable">
<button type="button" @click.prevent="showPage('new');">
{{ s__('IssueBoards|Create new board') }}
</button>
</li>
<li v-if="showDelete">
<button type="button" class="text-danger" @click.prevent="showPage('delete');">
{{ s__('IssueBoards|Delete board') }}
</button>
</li>
</ul>
</div>
</div>
</div>
<board-form
v-if="currentPage"
:milestone-path="milestonePath"
:labels-path="labelsPath"
:project-id="projectId"
:group-id="groupId"
:can-admin-board="canAdminBoard"
:scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
:weights="weights"
/>
</span>
</div>
</template>
......@@ -2,46 +2,14 @@
- milestone_filter_opts = { format: :json }
- milestone_filter_opts = milestone_filter_opts.merge(only_group_milestones: true) if board.group_board?
%boards-selector.js-boards-selector{ "inline-template" => true,
":current-board" => current_board_json,
"milestone-path" => milestones_filter_path(milestone_filter_opts) }
%span.boards-selector-wrapper.js-boards-selector-wrapper
.dropdown
%button.dropdown-menu-toggle.js-dropdown-toggle{ "v-on:click" => "loadBoards",
data: { toggle: "dropdown" } }
{{ board.name }}
= icon("chevron-down")
.dropdown-menu{ ":class" => "{ 'is-loading': loading }" }
.dropdown-content-faded-mask.js-scroll-fade{ ":class" => "scrollFadeClass" }
%ul.dropdown-list.js-dropdown-list{ "v-if" => "!loading", "v-on:scroll.passive" => "throttledSetScrollFade", ref: "content" }
%li.dropdown-item.js-dropdown-item{ "v-for" => "board in boards" }
%a{ ":href" => "'#{board_base_url}/' + board.id" }
{{ board.name }}
- if !multiple_boards_available? && current_board_parent.boards.size > 1
%li
.small.unclickable
Some of your boards are hidden, activate a license to see them again.
.dropdown-loading{ "v-if" => "loading" }
= icon("spin spinner")
- if can?(current_user, :admin_board, parent)
.dropdown-footer
%ul.dropdown-footer-list
- if parent.multiple_issue_boards_available?
%li
%a{ "href" => "#", "v-on:click.prevent" => "showPage('new')" }
Create new board
%li{ "v-if" => "showDelete" }
%a{ "href" => "#", "v-on:click.prevent" => "showPage('delete')" }
%span.text-danger
Delete board
%board-form{ ":milestone-path" => "milestonePath",
"labels-path" => labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: true),
":project-id" => "Number(#{@project&.id})",
":group-id" => "Number(#{@group&.id})",
":can-admin-board" => can?(current_user, :admin_board, parent),
":scoped-issue-board-feature-enabled" => parent.feature_available?(:scoped_issue_board) ? 'true' : 'false',
"weights" => [Issue::WEIGHT_ANY] + Issue.weight_options,
"v-if" => "currentPage" }
#js-multiple-boards-switcher.inline.boards-switcher{ data: { current_board: current_board_json,
milestone_path: milestones_filter_path(milestone_filter_opts),
board_base_url: board_base_url,
has_missing_boards: (!multiple_boards_available? && current_board_parent.boards.size > 1).to_s,
can_admin_board: can?(current_user, :admin_board, parent).to_s,
multiple_issue_boards_available: parent.multiple_issue_boards_available?.to_s,
labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: true),
project_id: @project&.id,
group_id: @group&.id,
scoped_issue_board_feature_enabled: parent.feature_available?(:scoped_issue_board) ? 'true' : 'false',
weights: ([Issue::WEIGHT_ANY] + Issue.weight_options).to_json } }
......@@ -51,7 +51,7 @@ describe 'Multiple Issue Boards', :js do
click_button board.name
page.within('.dropdown-menu') do
click_link 'Create new board'
click_button 'Create new board'
end
fill_in 'board-new-name', with: 'This is a new board'
......@@ -67,7 +67,7 @@ describe 'Multiple Issue Boards', :js do
wait_for_requests
page.within('.dropdown-menu') do
click_link 'Delete board'
click_button 'Delete board'
end
expect(page).to have_content('Are you sure you want to delete this board?')
......
......@@ -97,10 +97,10 @@ describe 'Scoped issue boards', :js do
visit group_boards_path(group)
wait_for_requests
expect(page).to have_css('#js-multiple-boards-switcher')
page.within '#js-multiple-boards-switcher' do
expect(page).to have_css('.js-boards-selector')
page.within '.js-boards-selector' do
find('.dropdown-menu-toggle').click
click_link 'Create new board'
click_button 'Create new board'
end
click_button 'Expand'
......@@ -433,10 +433,10 @@ describe 'Scoped issue boards', :js do
end
it "doesn't show the input when creating a board" do
page.within '#js-multiple-boards-switcher' do
page.within '.js-boards-selector' do
find('.dropdown-menu-toggle').click
click_link 'Create new board'
click_button 'Create new board'
# To make sure the form is shown
expect(page).to have_field('board-new-name')
......@@ -501,11 +501,11 @@ describe 'Scoped issue boards', :js do
end
def create_board_scope(filter, value)
page.within '#js-multiple-boards-switcher' do
page.within '.js-boards-selector' do
find('.dropdown-menu-toggle').click
end
click_link 'Create new board'
click_button 'Create new board'
find('#board-new-name').set 'test'
......
import Vue from 'vue';
import BoardService from 'ee/boards/services/board_service';
import BoardsSelector from 'ee/boards/components/boards_selector';
import BoardsSelector from 'ee/boards/components/boards_selector.vue';
import setTimeoutPromiseHelper from 'spec/helpers/set_timeout_promise_helper';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { TEST_HOST } from 'spec/test_constants';
const throttleDuration = 1;
......@@ -27,8 +28,7 @@ describe('BoardsSelector', () => {
});
beforeEach(done => {
loadFixtures('boards/show.html.raw');
setFixtures('<div class="js-boards-selector"></div>');
window.gl = window.gl || {};
window.gl.boardService = new BoardService({
......@@ -44,12 +44,29 @@ describe('BoardsSelector', () => {
spyOn(BoardService.prototype, 'allBoards').and.returnValue(boardServiceResponse);
const Component = Vue.extend(BoardsSelector);
vm = mountComponent(
BoardsSelector,
Component,
{
throttleDuration,
currentBoard: {},
milestonePath: '',
currentBoard: {
id: 1,
name: 'Development',
milestone_id: null,
weight: null,
assignee_id: null,
labels: [],
},
milestonePath: `${TEST_HOST}/milestone/path`,
boardBaseUrl: `${TEST_HOST}/board/base/url`,
hasMissingBoards: false,
canAdminBoard: true,
multipleIssueBoardsAvailable: true,
labelsPath: `${TEST_HOST}/labels/path`,
projectId: 42,
groupId: 19,
scopedIssueBoardFeatureEnabled: true,
weights: [],
},
document.querySelector('.js-boards-selector'),
);
......
......@@ -4577,6 +4577,15 @@ msgstr ""
msgid "IssueBoards|Boards"
msgstr ""
msgid "IssueBoards|Create new board"
msgstr ""
msgid "IssueBoards|Delete board"
msgstr ""
msgid "IssueBoards|Some of your boards are hidden, activate a license to see them again."
msgstr ""
msgid "Issues"
msgstr ""
......
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