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 @@ $(() => { ...@@ -115,6 +115,7 @@ $(() => {
this.state.lists = _.sortBy(this.state.lists, 'position'); this.state.lists = _.sortBy(this.state.lists, 'position');
Store.addBlankState(); Store.addBlankState();
Store.addPromotionState();
this.loading = false; this.loading = false;
}) })
.catch(() => new Flash('An error occurred. Please try again.')); .catch(() => new Flash('An error occurred. Please try again.'));
......
/* eslint-disable comma-dangle, space-before-function-paren, one-var */ /* eslint-disable comma-dangle, space-before-function-paren, one-var */
/* global Sortable */ /* global Sortable */
import Vue from 'vue'; import Vue from 'vue';
import boardPromotionState from 'ee/boards/components/board_promotion_state';
import AccessorUtilities from '../../lib/utils/accessor'; import AccessorUtilities from '../../lib/utils/accessor';
import boardList from './board_list'; import boardList from './board_list';
import boardBlankState from './board_blank_state'; import boardBlankState from './board_blank_state';
...@@ -17,6 +18,7 @@ gl.issueBoards.Board = Vue.extend({ ...@@ -17,6 +18,7 @@ gl.issueBoards.Board = Vue.extend({
boardList, boardList,
'board-delete': gl.issueBoards.BoardDelete, 'board-delete': gl.issueBoards.BoardDelete,
boardBlankState, boardBlankState,
boardPromotionState,
}, },
props: { props: {
list: Object, list: Object,
......
...@@ -12,7 +12,7 @@ class List { ...@@ -12,7 +12,7 @@ class List {
this.position = obj.position; this.position = obj.position;
this.title = obj.title; this.title = obj.title;
this.type = obj.list_type; 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.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1;
this.isExpanded = true; this.isExpanded = true;
this.page = 1; this.page = 1;
...@@ -26,7 +26,7 @@ class List { ...@@ -26,7 +26,7 @@ class List {
this.label = new ListLabel(obj.label); this.label = new ListLabel(obj.label);
} }
if (this.type !== 'blank' && this.id) { if (this.type !== 'blank' && this.type !== 'promotion' && this.id) {
this.getIssues().catch(() => { this.getIssues().catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global List */ /* global List */
import _ from 'underscore'; import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import boardsStoreEE from 'ee/boards/stores/boards_store_ee';
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
...@@ -140,3 +141,5 @@ gl.issueBoards.BoardsStore = { ...@@ -140,3 +141,5 @@ gl.issueBoards.BoardsStore = {
} }
}, },
}; };
boardsStoreEE.initEESpecific(gl.issueBoards.BoardsStore);
...@@ -227,12 +227,20 @@ ...@@ -227,12 +227,20 @@
} }
} }
.board-blank-state { .board-blank-state,
.board-promotion-state {
height: calc(100% - 49px); height: calc(100% - 49px);
padding: $gl-padding; padding: $gl-padding;
background-color: $white-light; background-color: $white-light;
} }
.board-promotion-state {
.btn.btn-primary {
display: block;
margin-bottom: 15px;
}
}
.board-blank-state-list { .board-blank-state-list {
list-style: none; list-style: none;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
%script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board" %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-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" = render "projects/issues/head"
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
class: "label color-label title", 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\") }" } ":style" => "{ backgroundColor: (list.label && list.label.color ? list.label.color : null), color: (list.label && list.label.color ? list.label.text_color : \"#2e2e2e\") }" }
{{ list.title }} {{ 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 }' } %span.issue-count-badge-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
{{ list.issuesSize }} {{ list.issuesSize }}
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :admin_issue, @project)
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
"v-if" => "!list.preset && list.id" } "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" } %button.board-delete.has-tooltip.pull-right{ type: "button", title: "Delete list", "aria-label" => "Delete list", data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash") = icon("trash")
%board-list{ "v-if" => 'list.type !== "blank"', %board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"',
":list" => "list", ":list" => "list",
":issues" => "list.issues", ":issues" => "list.issues",
":loading" => "list.loading", ":loading" => "list.loading",
...@@ -44,3 +44,4 @@ ...@@ -44,3 +44,4 @@
"ref" => "board-list" } "ref" => "board-list" }
- if can?(current_user, :admin_list, @project) - if can?(current_user, :admin_list, @project)
%board-blank-state{ "v-if" => 'list.id == "blank"' } %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 EE
module BoardsHelper module BoardsHelper
def board_data 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 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 ...@@ -204,6 +204,33 @@ describe 'Promotions', js: true do
end end
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 describe 'for issue export', js: true do
before do before do
allow(License).to receive(:current).and_return(nil) 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