Commit b9dea9ca authored by Clement Ho's avatar Clement Ho Committed by Sean McGivern

Add board scope highlight

parent c55155b3
...@@ -25,11 +25,11 @@ import './components/board_sidebar'; ...@@ -25,11 +25,11 @@ import './components/board_sidebar';
import './components/new_list_dropdown'; import './components/new_list_dropdown';
import './components/modal/index'; import './components/modal/index';
import '../vue_shared/vue_resource_interceptor'; import '../vue_shared/vue_resource_interceptor';
import { convertPermissionToBoolean } from '../lib/utils/common_utils';
import './components/boards_selector'; import './components/boards_selector';
import collapseIcon from './icons/fullscreen_collapse.svg'; import collapseIcon from './icons/fullscreen_collapse.svg';
import expandIcon from './icons/fullscreen_expand.svg'; import expandIcon from './icons/fullscreen_expand.svg';
import tooltip from '../vue_shared/directives/tooltip';
Vue.use(VueResource); Vue.use(VueResource);
...@@ -212,11 +212,14 @@ $(() => { ...@@ -212,11 +212,14 @@ $(() => {
el: configEl, el: configEl,
data() { data() {
return { return {
canAdminList: convertPermissionToBoolean( canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
this.$options.el.dataset.canAdminList, hasScope: this.$options.el.hasAttribute('data-has-scope'),
), state: Store.state,
}; };
}, },
directives: {
tooltip,
},
methods: { methods: {
showPage: page => gl.issueBoards.BoardsStore.showPage(page), showPage: page => gl.issueBoards.BoardsStore.showPage(page),
}, },
...@@ -224,11 +227,17 @@ $(() => { ...@@ -224,11 +227,17 @@ $(() => {
buttonText() { buttonText() {
return this.canAdminList ? 'Edit board' : 'View scope'; return this.canAdminList ? 'Edit board' : 'View scope';
}, },
tooltipTitle() {
return this.hasScope ? __('This board\'s scope is reduced') : '';
}
}, },
template: ` template: `
<div class="prepend-left-10"> <div class="prepend-left-10">
<button <button
v-tooltip
:title="tooltipTitle"
class="btn btn-inverted" class="btn btn-inverted"
:class="{ 'dot-highlight': hasScope }"
type="button" type="button"
@click.prevent="showPage('edit')" @click.prevent="showPage('edit')"
> >
...@@ -247,12 +256,8 @@ $(() => { ...@@ -247,12 +256,8 @@ $(() => {
modal: ModalStore.store, modal: ModalStore.store,
store: Store.state, store: Store.state,
isFullscreen: false, isFullscreen: false,
focusModeAvailable: convertPermissionToBoolean( focusModeAvailable: $boardApp.hasAttribute('data-focus-mode-available'),
$boardApp.dataset.focusModeAvailable, canAdminList: this.$options.el.hasAttribute('data-can-admin-list'),
),
canAdminList: this.$options.el && convertPermissionToBoolean(
this.$options.el.dataset.canAdminList,
),
}; };
}, },
watch: { watch: {
...@@ -320,7 +325,7 @@ $(() => { ...@@ -320,7 +325,7 @@ $(() => {
modal: ModalStore.store, modal: ModalStore.store,
store: Store.state, store: Store.state,
isFullscreen: false, isFullscreen: false,
focusModeAvailable: convertPermissionToBoolean($boardApp.dataset.focusModeAvailable), focusModeAvailable: $boardApp.hasAttribute('data-focus-mode-available'),
}, },
methods: { methods: {
toggleFocusMode() { toggleFocusMode() {
......
...@@ -243,6 +243,16 @@ ...@@ -243,6 +243,16 @@
} }
} }
&.dot-highlight::after {
content: '';
background-color: $blue-500;
width: $gl-padding * 0.5;
height: $gl-padding * 0.5;
display: inline-block;
border-radius: 50%;
margin-left: 3px;
}
svg { svg {
height: 15px; height: 15px;
width: 15px; width: 15px;
......
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
- if type == :boards - if type == :boards
- user_can_admin_list = can?(current_user, :admin_list, board.parent) - user_can_admin_list = can?(current_user, :admin_list, board.parent)
.js-board-config{ data: { can_admin_list: user_can_admin_list.to_s } } .js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } }
- if user_can_admin_list - if user_can_admin_list
.dropdown.prepend-left-10#js-add-list .dropdown.prepend-left-10#js-add-list
%button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data } %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data }
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
= render partial: "shared/issuable/label_page_create" = render partial: "shared/issuable/label_page_create"
= dropdown_loading = dropdown_loading
- if @project - if @project
#js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project).to_s } } #js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
#js-toggle-focus-btn.prepend-left-10 #js-toggle-focus-btn.prepend-left-10
- elsif type != :boards_modal - elsif type != :boards_modal
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
...@@ -81,13 +81,8 @@ class BoardsStoreEE { ...@@ -81,13 +81,8 @@ class BoardsStoreEE {
this.store.updateFiltersUrl(true); this.store.updateFiltersUrl(true);
} }
shouldAddPromotionState() {
// Decide whether to add the promotion state
return this.$boardApp.dataset.showPromotion === 'true';
}
addPromotion() { addPromotion() {
if (!this.shouldAddPromotionState() || this.promotionIsHidden() || this.store.disabled) return; if (!this.$boardApp.hasAttribute('data-show-promotion') || this.promotionIsHidden() || this.store.disabled) return;
this.store.addList({ this.store.addList({
id: 'promotion', id: 'promotion',
......
...@@ -8,7 +8,7 @@ module EE ...@@ -8,7 +8,7 @@ module EE
show_feature_promotion = (@project && show_promotions? && show_feature_promotion = (@project && show_promotions? &&
(!@project.feature_available?(:multiple_issue_boards) || (!@project.feature_available?(:multiple_issue_boards) ||
!@project.feature_available?(:scoped_issue_board) || !@project.feature_available?(:scoped_issue_board) ||
!@project.feature_available?(:issue_board_focus_mode))).to_s !@project.feature_available?(:issue_board_focus_mode)))
data = { data = {
board_milestone_title: board.milestone&.name, board_milestone_title: board.milestone&.name,
...@@ -17,7 +17,7 @@ module EE ...@@ -17,7 +17,7 @@ module EE
label_ids: board.label_ids, label_ids: board.label_ids,
labels: board.labels.to_json(only: [:id, :title, :color, :text_color] ), labels: board.labels.to_json(only: [:id, :title, :color, :text_color] ),
board_weight: board.weight, board_weight: board.weight,
focus_mode_available: parent.feature_available?(:issue_board_focus_mode).to_s, focus_mode_available: parent.feature_available?(:issue_board_focus_mode),
show_promotion: show_feature_promotion show_promotion: show_feature_promotion
} }
......
...@@ -2,6 +2,9 @@ module EE ...@@ -2,6 +2,9 @@ module EE
module Board module Board
extend ActiveSupport::Concern extend ActiveSupport::Concern
# Empty state for milestones and weights.
EMPTY_SCOPE_STATE = [nil, -1].freeze
prepended do prepended do
belongs_to :group belongs_to :group
belongs_to :milestone belongs_to :milestone
...@@ -45,6 +48,15 @@ module EE ...@@ -45,6 +48,15 @@ module EE
end end
end end
def scoped?
return false unless parent.feature_available?(:scoped_issue_board)
EMPTY_SCOPE_STATE.exclude?(milestone_id) ||
EMPTY_SCOPE_STATE.exclude?(weight) ||
labels.any? ||
assignee.present?
end
def as_json(options = {}) def as_json(options = {})
milestone_attrs = options.fetch(:include, {}) milestone_attrs = options.fetch(:include, {})
.extract!(:milestone) .extract!(:milestone)
......
...@@ -53,6 +53,12 @@ describe 'Scoped issue boards', :js do ...@@ -53,6 +53,12 @@ describe 'Scoped issue boards', :js do
expect(find('.tokens-container')).to have_content("") expect(find('.tokens-container')).to have_content("")
expect(page).to have_selector('.card', count: 3) expect(page).to have_selector('.card', count: 3)
end end
it 'displays dot highlight and tooltip' do
create_board_milestone(milestone.title)
expect_dot_highlight('Edit board')
end
end end
context 'labels' do context 'labels' do
...@@ -101,6 +107,12 @@ describe 'Scoped issue boards', :js do ...@@ -101,6 +107,12 @@ describe 'Scoped issue boards', :js do
end end
end end
end end
it 'displays dot highlight and tooltip' do
create_board_label(label_1.title)
expect_dot_highlight('Edit board')
end
end end
context 'assignee' do context 'assignee' do
...@@ -126,6 +138,12 @@ describe 'Scoped issue boards', :js do ...@@ -126,6 +138,12 @@ describe 'Scoped issue boards', :js do
expect(page).not_to have_css('.js-visual-token') expect(page).not_to have_css('.js-visual-token')
expect(page).to have_selector('.card', count: 3) expect(page).to have_selector('.card', count: 3)
end end
it 'displays dot highlight and tooltip' do
create_board_assignee(user.name)
expect_dot_highlight('Edit board')
end
end end
context 'weight' do context 'weight' do
...@@ -151,6 +169,12 @@ describe 'Scoped issue boards', :js do ...@@ -151,6 +169,12 @@ describe 'Scoped issue boards', :js do
expect(page).to have_selector('.card', count: 4) expect(page).to have_selector('.card', count: 4)
end end
it 'displays dot highlight and tooltip' do
create_board_weight(1)
expect_dot_highlight('Edit board')
end
end end
end end
...@@ -381,6 +405,10 @@ describe 'Scoped issue boards', :js do ...@@ -381,6 +405,10 @@ describe 'Scoped issue boards', :js do
expect(page).not_to have_button('Cancel') expect(page).not_to have_button('Cancel')
end end
end end
it 'does not display dot highlight and tooltip' do
expect_no_dot_highlight('View scope')
end
end end
context 'with scoped_issue_boards feature disabled' do context 'with scoped_issue_boards feature disabled' do
...@@ -394,6 +422,10 @@ describe 'Scoped issue boards', :js do ...@@ -394,6 +422,10 @@ describe 'Scoped issue boards', :js do
wait_for_requests wait_for_requests
end end
it 'does not display dot highlight and tooltip' do
expect_no_dot_highlight('Edit board')
end
it "doesn't show the input when creating a board" do it "doesn't show the input when creating a board" do
page.within '#js-multiple-boards-switcher' do page.within '#js-multiple-boards-switcher' do
find('.dropdown-menu-toggle').click find('.dropdown-menu-toggle').click
...@@ -412,6 +444,20 @@ describe 'Scoped issue boards', :js do ...@@ -412,6 +444,20 @@ describe 'Scoped issue boards', :js do
end end
end end
def expect_dot_highlight(button_title)
button = first('.filter-dropdown-container .btn.btn-inverted')
expect(button.text).to include(button_title)
expect(button[:class]).to include('dot-highlight')
expect(button['data-original-title']).to include('This board\'s scope is reduced')
end
def expect_no_dot_highlight(button_title)
button = first('.filter-dropdown-container .btn.btn-inverted')
expect(button.text).to include(button_title)
expect(button[:class]).not_to include('dot-highlight')
expect(button['data-original-title']).not_to include('This board\'s scope is reduced')
end
# Create board helper methods # Create board helper methods
# #
def create_board_milestone(milestone_title) def create_board_milestone(milestone_title)
......
...@@ -61,4 +61,49 @@ describe Board do ...@@ -61,4 +61,49 @@ describe Board do
expect(board.milestone).to be_nil expect(board.milestone).to be_nil
end end
end end
describe '#scoped?' do
before do
stub_licensed_features(scoped_issue_board: true)
end
it 'returns true when milestone is not nil AND is not "Any milestone"' do
milestone = create(:milestone)
board = create(:board, milestone: milestone, weight: nil, labels: [], assignee: nil)
expect(board).to be_scoped
end
it 'returns true when weight is not nil AND is not "Any weight"' do
board = create(:board, milestone: nil, weight: 2, labels: [], assignee: nil)
expect(board).to be_scoped
end
it 'returns true when any label exists' do
board = create(:board, milestone: nil, weight: nil, assignee: nil)
board.labels.create!(title: 'foo')
expect(board).to be_scoped
end
it 'returns true when assignee is present' do
user = create(:user)
board = create(:board, milestone: nil, weight: nil, labels: [], assignee: user)
expect(board).to be_scoped
end
it 'returns false when feature is not available' do
stub_licensed_features(scoped_issue_board: false)
expect(board).not_to be_scoped
end
it 'returns false when board is not scoped' do
board = create(:board, milestone_id: -1, weight: -1, labels: [], assignee: nil)
expect(board).not_to be_scoped
end
end
end end
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