Commit 9a8075d3 authored by Phil Hughes's avatar Phil Hughes

Merge branch '2899-promote-issue-board' into 'master'

Resolve "Promote issue board"

Closes #2899

See merge request !2579
parents c4fb85c0 f63e5464
......@@ -115,6 +115,7 @@ $(() => {
this.state.lists = _.sortBy(this.state.lists, 'position');
Store.addBlankState();
Store.addPromotionState();
this.loading = false;
})
.catch(() => new Flash('An error occurred. Please try again.'));
......
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
/* global Sortable */
import Vue from 'vue';
import boardPromotionState from 'ee/boards/components/board_promotion_state';
import AccessorUtilities from '../../lib/utils/accessor';
import boardList from './board_list';
import boardBlankState from './board_blank_state';
......@@ -17,6 +18,7 @@ gl.issueBoards.Board = Vue.extend({
boardList,
'board-delete': gl.issueBoards.BoardDelete,
boardBlankState,
boardPromotionState,
},
props: {
list: Object,
......
......@@ -12,7 +12,7 @@ class List {
this.position = obj.position;
this.title = obj.title;
this.type = obj.list_type;
this.preset = ['backlog', 'closed', 'blank'].indexOf(this.type) > -1;
this.preset = ['backlog', 'closed', 'blank', 'promotion'].indexOf(this.type) > -1;
this.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1;
this.isExpanded = true;
this.page = 1;
......@@ -26,7 +26,7 @@ class List {
this.label = new ListLabel(obj.label);
}
if (this.type !== 'blank' && this.id) {
if (this.type !== 'blank' && this.type !== 'promotion' && this.id) {
this.getIssues().catch(() => {
// TODO: handle request error
});
......
......@@ -2,6 +2,7 @@
/* global List */
import _ from 'underscore';
import Cookies from 'js-cookie';
import boardsStoreEE from 'ee/boards/stores/boards_store_ee';
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
......@@ -140,3 +141,5 @@ gl.issueBoards.BoardsStore = {
}
},
};
boardsStoreEE.initEESpecific(gl.issueBoards.BoardsStore);
......@@ -227,12 +227,20 @@
}
}
.board-blank-state {
.board-blank-state,
.board-promotion-state {
height: calc(100% - 49px);
padding: $gl-padding;
background-color: $white-light;
}
.board-promotion-state {
.btn.btn-primary {
display: block;
margin-bottom: 15px;
}
}
.board-blank-state-list {
list-style: none;
......
......@@ -12,6 +12,7 @@
%script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board"
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal
%script#js-board-promotion{ type: "text/x-template" }= render "shared/promotions/promote_issue_board"
= render "projects/issues/head"
......
......@@ -17,7 +17,7 @@
class: "label color-label title",
":style" => "{ backgroundColor: (list.label && list.label.color ? list.label.color : null), color: (list.label && list.label.color ? list.label.text_color : \"#2e2e2e\") }" }
{{ list.title }}
.issue-count-badge.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
.issue-count-badge.pull-right.clearfix{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"' }
%span.issue-count-badge-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
{{ list.issuesSize }}
- if can?(current_user, :admin_issue, @project)
......@@ -34,7 +34,7 @@
"v-if" => "!list.preset && list.id" }
%button.board-delete.has-tooltip.pull-right{ type: "button", title: "Delete list", "aria-label" => "Delete list", data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash")
%board-list{ "v-if" => 'list.type !== "blank"',
%board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"',
":list" => "list",
":issues" => "list.issues",
":loading" => "list.loading",
......@@ -44,3 +44,4 @@
"ref" => "board-list" }
- if can?(current_user, :admin_list, @project)
%board-blank-state{ "v-if" => 'list.id == "blank"' }
%board-promotion-state{ "v-if" => 'list.id == "promotion"' }
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><g transform="translate(12 25)"><path fill="#E1DBF2" d="M3 0h10a3 3 0 0 1 3 3v22a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm1 4v20h8V4H4zm2 2h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"/><rect width="6" height="4" x="5" y="12" fill="#6B4FBB" rx="1"/></g><g transform="translate(50 25)"><rect width="6" height="4" x="5" y="6" fill="#6B4FBB" rx="1"/><path fill="#E1DBF2" fill-rule="nonzero" d="M3 0h10a3 3 0 0 1 3 3v22a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3a3 3 0 0 1 3-3zm1 4v20h8V4H4zm2 8h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></g><path fill="#E1DBF2" d="M34 25h10a3 3 0 0 1 3 3v28a3 3 0 0 1-3 3H34a3 3 0 0 1-3-3V28a3 3 0 0 1 3-3zm1 4v26h8V29h-8zm2 8h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/><path fill="#6B4FBB" d="M37 43h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1zm0-12h4a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1z"/></g></svg>
\ No newline at end of file
const Store = gl.issueBoards.BoardsStore;
export default {
template: '#js-board-promotion',
methods: {
clearPromotionState: Store.removePromotionState.bind(Store),
},
};
/* eslint-disable class-methods-use-this */
import Cookies from 'js-cookie';
class BoardsStoreEE {
initEESpecific(boardsStore) {
this.$boardApp = document.getElementById('board-app');
this.store = boardsStore;
this.store.addPromotionState = () => {
this.addPromotion();
};
this.store.removePromotionState = () => {
this.removePromotion();
};
}
shouldAddPromotionState() {
// Decide whether to add the promotion state
return this.$boardApp.dataset.showPromotion === 'true';
}
addPromotion() {
if (!this.shouldAddPromotionState() || this.promotionIsHidden() || this.store.disabled) return;
this.store.addList({
id: 'promotion',
list_type: 'promotion',
title: 'Improve Issue boards',
position: 0,
});
this.store.state.lists = _.sortBy(this.store.state.lists, 'position');
}
removePromotion() {
this.store.removeList('promotion', 'promotion');
Cookies.set('promotion_issue_board_hidden', 'true', {
expires: 365 * 10,
path: '',
});
}
promotionIsHidden() {
return Cookies.get('promotion_issue_board_hidden') === 'true';
}
}
export default new BoardsStoreEE();
module EE
module BoardsHelper
def board_data
super.merge(focus_mode_available: @project.feature_available?(:issue_board_focus_mode).to_s)
super.merge(focus_mode_available: @project.feature_available?(:issue_board_focus_mode).to_s,
show_promotion: (show_promotions? && (!@project.feature_available?(:multiple_issue_boards) || !@project.feature_available?(:issue_board_milestone) || !@project.feature_available?(:issue_board_focus_mode))).to_s)
end
end
end
.board-promotion-state
.svg-container.center
= custom_icon('icon_issue_board')
%p
- if current_application_settings.should_check_namespace_plan?
= _('Upgrade your plan to improve Issue boards.')
- else
= _('Improve Issue boards with GitLab Enterprise Edition.')
%ul
- unless @project.feature_available?(:multiple_issue_boards)
%li
= link_to _('Multiple issue boards'), help_page_path('user/project/issue_board.html', anchor:'use-cases-for-multiple-issue-boards'), target: '_blank'
- unless @project.feature_available?(:issue_board_milestone)
%li
= link_to _('Issue boards with milestones'), help_page_path('user/project/issue_board.html', anchor:'board-with-a-milestone'), target: '_blank'
- unless @project.feature_available?(:issue_board_focus_mode)
%li
= link_to _('Issue board focus mode'), help_page_path('user/project/issue_board.html', anchor:'focus-mode'), target: '_blank'
= render 'shared/promotions/promotion_link_project'
.top-space
%button.btn.btn-default.btn-block#hide-btn{ :href => "#", "@click.stop" => "clearPromotionState" }
= _("Thanks! Don't show me this again")
......@@ -204,6 +204,33 @@ describe 'Promotions', js: true do
end
end
describe 'for issue boards ', js: true do
before do
stub_application_setting(check_namespace_plan: true)
allow(Gitlab).to receive(:com?) { true }
project.team << [user, :master]
sign_in(user)
end
it 'should appear in milestone page' do
visit project_boards_path(project)
expect(find('.board-promotion-state')).to have_content "Upgrade your plan to improve Issue boards"
end
it 'does not show when cookie is set' do
visit project_boards_path(project)
within('.board-promotion-state') do
find('#hide-btn').trigger('click')
end
visit project_boards_path(project, milestone)
expect(page).not_to have_selector('.board-promotion-state')
end
end
describe 'for issue export', js: true do
before do
allow(License).to receive(:current).and_return(nil)
......
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