Commit 5b893e91 authored by Simon Knox's avatar Simon Knox Committed by Ezekiel Kigbo

Add checkbox to scope board to current iteration

Add some basic feature specs
parent 5e154e44
...@@ -83,6 +83,10 @@ export function fullBoardId(boardId) { ...@@ -83,6 +83,10 @@ export function fullBoardId(boardId) {
return `gid://gitlab/Board/${boardId}`; return `gid://gitlab/Board/${boardId}`;
} }
export function fullIterationId(id) {
return `gid://gitlab/Iteration/${id}`;
}
export function fullLabelId(label) { export function fullLabelId(label) {
if (label.project_id !== null) { if (label.project_id !== null) {
return `gid://gitlab/ProjectLabel/${label.id}`; return `gid://gitlab/ProjectLabel/${label.id}`;
...@@ -141,6 +145,7 @@ export default { ...@@ -141,6 +145,7 @@ export default {
formatListIssues, formatListIssues,
fullBoardId, fullBoardId,
fullLabelId, fullLabelId,
fullIterationId,
getBoardsPath, getBoardsPath,
isListDraggable, isListDraggable,
}; };
...@@ -15,6 +15,7 @@ const boardDefaults = { ...@@ -15,6 +15,7 @@ const boardDefaults = {
name: '', name: '',
labels: [], labels: [],
milestone_id: undefined, milestone_id: undefined,
iteration_id: undefined,
assignee: {}, assignee: {},
assignee_id: undefined, assignee_id: undefined,
weight: null, weight: null,
...@@ -171,6 +172,9 @@ export default { ...@@ -171,6 +172,9 @@ export default {
} }
}, },
methods: { methods: {
setIteration(iterationId) {
this.board.iteration_id = iterationId;
},
callBoardMutation(id) { callBoardMutation(id) {
return this.$apollo.mutate({ return this.$apollo.mutate({
mutation: createBoardMutation, mutation: createBoardMutation,
...@@ -289,6 +293,7 @@ export default { ...@@ -289,6 +293,7 @@ export default {
:project-id="projectId" :project-id="projectId"
:group-id="groupId" :group-id="groupId"
:weights="weights" :weights="weights"
@set-iteration="setIteration"
/> />
</form> </form>
</gl-modal> </gl-modal>
......
mutation UpdateBoard($id: ID!, $hideClosedList: Boolean, $hideBacklogList: Boolean) { mutation UpdateBoard($id: BoardID!, $hideClosedList: Boolean, $hideBacklogList: Boolean) {
updateBoard( updateBoard(
input: { id: $id, hideClosedList: $hideClosedList, hideBacklogList: $hideBacklogList } input: { id: $id, hideClosedList: $hideClosedList, hideBacklogList: $hideBacklogList }
) { ) {
......
...@@ -4,7 +4,7 @@ group: Project Management ...@@ -4,7 +4,7 @@ group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
--- ---
# Issue Boards # Issue Boards **(CORE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5554) in [GitLab 8.11](https://about.gitlab.com/releases/2016/08/22/gitlab-8-11-released/#issue-board). > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5554) in [GitLab 8.11](https://about.gitlab.com/releases/2016/08/22/gitlab-8-11-released/#issue-board).
...@@ -233,17 +233,18 @@ advanced functionality is present in [higher tiers only](https://about.gitlab.co ...@@ -233,17 +233,18 @@ advanced functionality is present in [higher tiers only](https://about.gitlab.co
### Configurable issue boards **(STARTER)** ### Configurable issue boards **(STARTER)**
> [Introduced](https://about.gitlab.com/releases/2017/11/22/gitlab-10-2-released/#issue-boards-configuration) in [GitLab Starter](https://about.gitlab.com/pricing/) 10.2. > - [Introduced](https://about.gitlab.com/releases/2017/11/22/gitlab-10-2-released/#issue-boards-configuration) in GitLab 10.2.
> - Setting current iteration as scope [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196804) in GitLab 13.7.
An issue board can be associated with a GitLab [Milestone](milestones/index.md#milestones), An issue board can be associated with a [milestone](milestones/index.md#milestones),
[Labels](labels.md), Assignee and Weight [labels](labels.md), assignee, weight, and current [iteration](../group/iterations/index.md),
which automatically filter the board issues accordingly. which automatically filter the board issues accordingly.
This allows you to create unique boards according to your team's need. This allows you to create unique boards according to your team's need.
![Create scoped board](img/issue_board_creation_v13_6.png) ![Create scoped board](img/issue_board_creation_v13_6.png)
You can define the scope of your board when creating it or by clicking the **Edit board** button. You can define the scope of your board when creating it or by clicking the **Edit board** button.
After a milestone, assignee or weight is assigned to an issue board, you can no longer After a milestone, iteration, assignee, or weight is assigned to an issue board, you can no longer
filter through these in the search bar. In order to do that, you need to remove the desired scope filter through these in the search bar. In order to do that, you need to remove the desired scope
(for example, milestone, assignee, or weight) from the issue board. (for example, milestone, assignee, or weight) from the issue board.
......
...@@ -2,15 +2,17 @@ ...@@ -2,15 +2,17 @@
import { __ } from '~/locale'; import { __ } from '~/locale';
import ListLabel from '~/boards/models/label'; import ListLabel from '~/boards/models/label';
import BoardLabelsSelect from '~/vue_shared/components/sidebar/labels_select/base.vue'; import BoardLabelsSelect from '~/vue_shared/components/sidebar/labels_select/base.vue';
import AssigneeSelect from './assignee_select.vue';
import BoardMilestoneSelect from './milestone_select.vue'; import BoardMilestoneSelect from './milestone_select.vue';
import BoardScopeCurrentIteration from './board_scope_current_iteration.vue';
import BoardWeightSelect from './weight_select.vue'; import BoardWeightSelect from './weight_select.vue';
import AssigneeSelect from './assignee_select.vue';
export default { export default {
components: { components: {
AssigneeSelect, AssigneeSelect,
BoardLabelsSelect, BoardLabelsSelect,
BoardMilestoneSelect, BoardMilestoneSelect,
BoardScopeCurrentIteration,
BoardWeightSelect, BoardWeightSelect,
}, },
...@@ -111,6 +113,12 @@ export default { ...@@ -111,6 +113,12 @@ export default {
:can-edit="canAdminBoard" :can-edit="canAdminBoard"
/> />
<board-scope-current-iteration
:can-admin-board="canAdminBoard"
:iteration-id="board.iteration_id"
@set-iteration="$emit('set-iteration', $event)"
/>
<board-labels-select <board-labels-select
:context="board" :context="board"
:labels-path="labelsPath" :labels-path="labelsPath"
......
<script>
import { GlFormCheckbox } from '@gitlab/ui';
import { __ } from '~/locale';
import { IterationIDs } from '../constants';
export default {
i18n: {
label: __('Scope board to current iteration'),
title: __('Iteration'),
},
components: {
GlFormCheckbox,
},
props: {
canAdminBoard: {
type: Boolean,
required: true,
},
iterationId: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
checked: this.iterationId === IterationIDs.CURRENT,
};
},
methods: {
handleToggle() {
this.checked = !this.checked;
const iterationId = this.checked ? IterationIDs.CURRENT : null;
this.$emit('set-iteration', iterationId);
},
},
};
</script>
<template>
<div class="block iteration">
<div class="title gl-mb-3">
{{ $options.i18n.title }}
</div>
<gl-form-checkbox
:disabled="!canAdminBoard"
:checked="checked"
class="gl-text-gray-500"
data-testid="scope-to-current-iteration"
@change="handleToggle"
>{{ $options.i18n.label }}
</gl-form-checkbox>
</div>
</template>
...@@ -14,6 +14,7 @@ export const IterationFilterType = { ...@@ -14,6 +14,7 @@ export const IterationFilterType = {
export const IterationIDs = { export const IterationIDs = {
NONE: 0, NONE: 0,
CURRENT: -4,
}; };
export const MilestoneFilterType = { export const MilestoneFilterType = {
......
...@@ -5,6 +5,7 @@ module EE ...@@ -5,6 +5,7 @@ module EE
prepended do prepended do
expose :milestone_id expose :milestone_id
expose :iteration_id
expose :weight expose :weight
expose :label_ids expose :label_ids
expose :milestone, using: BoardMilestoneEntity expose :milestone, using: BoardMilestoneEntity
......
---
title: Add current iteration toggle to board config
merge_request: 46514
author:
type: added
...@@ -271,6 +271,30 @@ RSpec.describe 'Scoped issue boards', :js do ...@@ -271,6 +271,30 @@ RSpec.describe 'Scoped issue boards', :js do
end end
end end
context 'iteration' do
context 'board not scoped to iteration' do
it 'sets board to current iteration' do
expect(page).to have_selector('.board-card', count: 3)
update_board_scope('current_iteration', true)
expect(page).to have_selector('.board-card', count: 0)
end
end
context 'board scoped to current iteration' do
it 'removes current iteration from board' do
create_board_scope('current_iteration', true)
expect(page).to have_selector('.board-card', count: 0)
update_board_scope('current_iteration', false)
expect(page).to have_selector('.board-card', count: 3)
end
end
end
context 'labels' do context 'labels' do
let!(:label_1) { create(:label, project: project, name: 'Label 1') } let!(:label_1) { create(:label, project: project, name: 'Label 1') }
let!(:label_2) { create(:label, project: project, name: 'Label 2') } let!(:label_2) { create(:label, project: project, name: 'Label 2') }
...@@ -526,19 +550,7 @@ RSpec.describe 'Scoped issue boards', :js do ...@@ -526,19 +550,7 @@ RSpec.describe 'Scoped issue boards', :js do
click_button 'Expand' click_button 'Expand'
page.within(".#{filter}") do click_value(filter, value)
click_button 'Edit'
if value.is_a?(Array)
value.each { |value| click_link value }
elsif filter == 'weight'
page.within(".dropdown-menu") do
click_button value
end
else
click_link value
end
end
click_on_board_modal click_on_board_modal
...@@ -549,6 +561,31 @@ RSpec.describe 'Scoped issue boards', :js do ...@@ -549,6 +561,31 @@ RSpec.describe 'Scoped issue boards', :js do
expect(page).not_to have_selector('.board-list-loading') expect(page).not_to have_selector('.board-list-loading')
end end
def click_value(filter, value)
if filter == 'current_iteration'
current_iteration_checkbox = 'Scope board to current iteration'
if value
check(current_iteration_checkbox)
else
uncheck(current_iteration_checkbox)
end
else
page.within(".#{filter}") do
click_button 'Edit'
if value.is_a?(Array)
value.each { |value| click_link value }
elsif filter == 'weight'
page.within(".dropdown-menu") do
click_button value
end
else
click_link value
end
end
end
end
def click_on_create_new_board def click_on_create_new_board
page.within '.js-boards-selector' do page.within '.js-boards-selector' do
find('.dropdown-menu-toggle').click find('.dropdown-menu-toggle').click
...@@ -561,13 +598,7 @@ RSpec.describe 'Scoped issue boards', :js do ...@@ -561,13 +598,7 @@ RSpec.describe 'Scoped issue boards', :js do
def update_board_scope(filter, value) def update_board_scope(filter, value)
edit_board.click edit_board.click
page.within(".#{filter}") do click_value(filter, value)
click_button 'Edit'
page.within(".dropdown-menu") do
filter == 'weight' ? click_button(value) : click_link(value)
end
end
click_on_board_modal click_on_board_modal
......
...@@ -24208,6 +24208,9 @@ msgstr "" ...@@ -24208,6 +24208,9 @@ msgstr ""
msgid "Scope" msgid "Scope"
msgstr "" msgstr ""
msgid "Scope board to current iteration"
msgstr ""
msgid "Scopes" msgid "Scopes"
msgstr "" 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