diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index c9a5ee2ce8580bbf847263b8aff2ac53c2eed2ae..57112ab4fb4a9afb80d78f264c62c02ead35602a 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -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, + }); + }, }); }; diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 824bbe3524bd64e48c96645c5841ae097cdff33b..6939aba68964b31638c227844d16727a74e33afe 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -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] diff --git a/ee/app/assets/javascripts/boards/components/board_form.vue b/ee/app/assets/javascripts/boards/components/board_form.vue index 342b16d12106dea715d89db08307e6d77c26a82d..6aab5af66a7c10acad2a233bf90b46f5d5846100 100644 --- a/ee/app/assets/javascripts/boards/components/board_form.vue +++ b/ee/app/assets/javascripts/boards/components/board_form.vue @@ -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> diff --git a/ee/app/assets/javascripts/boards/components/boards_selector.js b/ee/app/assets/javascripts/boards/components/boards_selector.vue similarity index 54% rename from ee/app/assets/javascripts/boards/components/boards_selector.js rename to ee/app/assets/javascripts/boards/components/boards_selector.vue index 9feed8c4f448e7344fbca3db2f6b5148a6044817..0f777f7dd5606c035b2ea69075e51706c4ec532e 100644 --- a/ee/app/assets/javascripts/boards/components/boards_selector.js +++ b/ee/app/assets/javascripts/boards/components/boards_selector.vue @@ -1,15 +1,19 @@ -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> diff --git a/ee/app/views/shared/boards/_switcher.html.haml b/ee/app/views/shared/boards/_switcher.html.haml index 389f2fae12df359795c3aaf29aa669349ed3eade..7d4f6b6ec6283ddc8f5af096ffb466dee2341fcc 100644 --- a/ee/app/views/shared/boards/_switcher.html.haml +++ b/ee/app/views/shared/boards/_switcher.html.haml @@ -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 } } diff --git a/ee/spec/features/boards/multiple_boards_spec.rb b/ee/spec/features/boards/multiple_boards_spec.rb index 5942d358992f913b2bd42366a15fd98dc7a003a0..a3dedc30b61901fe31f4c988050c5d6bd0586b4f 100644 --- a/ee/spec/features/boards/multiple_boards_spec.rb +++ b/ee/spec/features/boards/multiple_boards_spec.rb @@ -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?') diff --git a/ee/spec/features/boards/scoped_issue_board_spec.rb b/ee/spec/features/boards/scoped_issue_board_spec.rb index 41d4dea9db54ed38022afacbb52ca96306bdb09c..8dc0b5d9d533e5e8285d1d1fface23affaff8c8a 100644 --- a/ee/spec/features/boards/scoped_issue_board_spec.rb +++ b/ee/spec/features/boards/scoped_issue_board_spec.rb @@ -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' diff --git a/ee/spec/javascripts/boards/components/boards_selector_spec.js b/ee/spec/javascripts/boards/components/boards_selector_spec.js index da810298adb1a0b93ccaf53a2929c61f2df4ba7e..ab6c023c011457f6e00e8e7b334caa14f23cc49d 100644 --- a/ee/spec/javascripts/boards/components/boards_selector_spec.js +++ b/ee/spec/javascripts/boards/components/boards_selector_spec.js @@ -1,8 +1,9 @@ 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'), ); diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b19a7f46ef5c39b8a80aa3f83f9a2cdc0fdf9d91..f9ebf49a2cb9f3a24ed2b8c89c7ece5d1eb15603 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 ""