Commit 8aafdd0d authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '238222-clean-up-boards-with-swimlanes-feature-flag' into 'master'

Remove boards_with_swimlanes feature flag [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!48306
parents b491ece0 16f9857b
...@@ -32,9 +32,9 @@ export default { ...@@ -32,9 +32,9 @@ export default {
...mapState(['boardLists', 'error']), ...mapState(['boardLists', 'error']),
...mapGetters(['isSwimlanesOn']), ...mapGetters(['isSwimlanesOn']),
boardListsToUse() { boardListsToUse() {
const lists = return this.glFeatures.graphqlBoardLists || this.isSwimlanesOn
this.glFeatures.graphqlBoardLists || this.isSwimlanesOn ? this.boardLists : this.lists; ? sortBy([...Object.values(this.boardLists)], 'position')
return sortBy([...Object.values(lists)], 'position'); : this.lists;
}, },
}, },
mounted() { mounted() {
...@@ -53,11 +53,7 @@ export default { ...@@ -53,11 +53,7 @@ export default {
<gl-alert v-if="error" variant="danger" :dismissible="false"> <gl-alert v-if="error" variant="danger" :dismissible="false">
{{ error }} {{ error }}
</gl-alert> </gl-alert>
<div <div v-if="!isSwimlanesOn" class="boards-list gl-w-full gl-py-5 gl-px-3 gl-white-space-nowrap">
v-if="!isSwimlanesOn"
class="boards-list gl-w-full gl-py-5 gl-px-3 gl-white-space-nowrap"
data-qa-selector="boards_list"
>
<board-column <board-column
v-for="list in boardListsToUse" v-for="list in boardListsToUse"
:key="list.id" :key="list.id"
......
...@@ -278,7 +278,7 @@ export default { ...@@ -278,7 +278,7 @@ export default {
v-if="isSwimlanesHeader && !list.isExpanded" v-if="isSwimlanesHeader && !list.isExpanded"
ref="collapsedInfo" ref="collapsedInfo"
aria-hidden="true" aria-hidden="true"
class="board-header-collapsed-info-icon gl-mt-2 gl-cursor-pointer gl-text-gray-500" class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-gray-500"
> >
<gl-icon name="information" /> <gl-icon name="information" />
</span> </span>
......
import FilteredSearchBoards from '../../filtered_search_boards'; import FilteredSearchBoards from '../../filtered_search_boards';
import FilteredSearchContainer from '../../../filtered_search/container'; import FilteredSearchContainer from '../../../filtered_search/container';
import vuexstore from '~/boards/stores';
export default { export default {
name: 'modal-filters', name: 'modal-filters',
...@@ -12,7 +13,7 @@ export default { ...@@ -12,7 +13,7 @@ export default {
mounted() { mounted() {
FilteredSearchContainer.container = this.$el; FilteredSearchContainer.container = this.$el;
this.filteredSearch = new FilteredSearchBoards(this.store); this.filteredSearch = new FilteredSearchBoards(this.store, vuexstore);
this.filteredSearch.setup(); this.filteredSearch.setup();
this.filteredSearch.removeTokens(); this.filteredSearch.removeTokens();
this.filteredSearch.handleInputPlaceholder(); this.filteredSearch.handleInputPlaceholder();
......
...@@ -4,7 +4,7 @@ import FilteredSearchContainer from '../filtered_search/container'; ...@@ -4,7 +4,7 @@ import FilteredSearchContainer from '../filtered_search/container';
import boardsStore from './stores/boards_store'; import boardsStore from './stores/boards_store';
export default class FilteredSearchBoards extends FilteredSearchManager { export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) { constructor(store, vuexstore, updateUrl = false, cantEdit = []) {
super({ super({
page: 'boards', page: 'boards',
isGroupDecendent: true, isGroupDecendent: true,
...@@ -22,18 +22,18 @@ export default class FilteredSearchBoards extends FilteredSearchManager { ...@@ -22,18 +22,18 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
this.isHandledAsync = true; this.isHandledAsync = true;
this.cantEdit = cantEdit.filter(i => typeof i === 'string'); this.cantEdit = cantEdit.filter(i => typeof i === 'string');
this.cantEditWithValue = cantEdit.filter(i => typeof i === 'object'); this.cantEditWithValue = cantEdit.filter(i => typeof i === 'object');
this.vuexstore = vuexstore;
} }
updateObject(path) { updateObject(path) {
const groupByParam = new URLSearchParams(window.location.search).get('group_by'); const groupByParam = new URLSearchParams(window.location.search).get('group_by');
this.store.path = `${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`; this.store.path = `${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`;
if (gon.features.boardsWithSwimlanes || gon.features.graphqlBoardLists) { if (this.vuexstore.getters.shouldUseGraphQL) {
boardsStore.updateFiltersUrl(); boardsStore.updateFiltersUrl();
boardsStore.performSearch(); boardsStore.performSearch();
} } else if (this.updateUrl) {
if (this.updateUrl) {
boardsStore.updateFiltersUrl(); boardsStore.updateFiltersUrl();
} }
} }
......
import Vue from 'vue'; import Vue from 'vue';
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import 'ee_else_ce/boards/models/issue'; import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list'; import 'ee_else_ce/boards/models/list';
...@@ -77,7 +77,6 @@ export default () => { ...@@ -77,7 +77,6 @@ export default () => {
el: $boardApp, el: $boardApp,
components: { components: {
BoardContent, BoardContent,
Board: () => import('ee_else_ce/boards/components/board_column.vue'),
BoardSidebar, BoardSidebar,
BoardAddIssuesModal, BoardAddIssuesModal,
BoardSettingsSidebar: () => import('~/boards/components/board_settings_sidebar.vue'), BoardSettingsSidebar: () => import('~/boards/components/board_settings_sidebar.vue'),
...@@ -114,8 +113,7 @@ export default () => { ...@@ -114,8 +113,7 @@ export default () => {
}; };
}, },
computed: { computed: {
...mapState(['isShowingEpicsSwimlanes']), ...mapGetters(['isSwimlanesOn', 'shouldUseGraphQL']),
...mapGetters(['shouldUseGraphQL']),
detailIssueVisible() { detailIssueVisible() {
return Object.keys(this.detailIssue.issue).length; return Object.keys(this.detailIssue.issue).length;
}, },
...@@ -154,7 +152,12 @@ export default () => { ...@@ -154,7 +152,12 @@ export default () => {
eventHub.$off('initialBoardLoad', this.initialBoardLoad); eventHub.$off('initialBoardLoad', this.initialBoardLoad);
}, },
mounted() { mounted() {
this.filterManager = new FilteredSearchBoards(boardsStore.filter, true, boardsStore.cantEdit); this.filterManager = new FilteredSearchBoards(
boardsStore.filter,
store,
true,
boardsStore.cantEdit,
);
this.filterManager.setup(); this.filterManager.setup();
this.performSearch(); this.performSearch();
...@@ -193,11 +196,11 @@ export default () => { ...@@ -193,11 +196,11 @@ export default () => {
}, },
performSearch() { performSearch() {
this.setFilters(convertObjectPropsToCamelCase(urlParamsToObject(window.location.search))); this.setFilters(convertObjectPropsToCamelCase(urlParamsToObject(window.location.search)));
if (gon.features.boardsWithSwimlanes && this.isShowingEpicsSwimlanes) { if (this.isSwimlanesOn) {
this.resetEpics(); this.resetEpics();
this.resetIssues(); this.resetIssues();
this.fetchEpicsSwimlanes({}); this.fetchEpicsSwimlanes({});
} else if (gon.features.graphqlBoardLists && !this.isShowingEpicsSwimlanes) { } else if (gon.features.graphqlBoardLists) {
this.fetchLists(); this.fetchLists();
this.resetIssues(); this.resetIssues();
} }
......
...@@ -302,11 +302,7 @@ const boardsStore = { ...@@ -302,11 +302,7 @@ const boardsStore = {
onNewListIssueResponse(list, issue, data) { onNewListIssueResponse(list, issue, data) {
issue.refreshData(data); issue.refreshData(data);
if ( if (list.issues.length > 1) {
!gon.features.boardsWithSwimlanes &&
!gon.features.graphqlBoardLists &&
list.issues.length > 1
) {
const moveBeforeId = list.issues[1].id; const moveBeforeId = list.issues[1].id;
this.moveIssue(issue.id, null, null, null, moveBeforeId); this.moveIssue(issue.id, null, null, null, moveBeforeId);
} }
......
...@@ -4,13 +4,7 @@ import { inactiveId } from '../constants'; ...@@ -4,13 +4,7 @@ import { inactiveId } from '../constants';
export default { export default {
labelToggleState: state => (state.isShowingLabels ? 'on' : 'off'), labelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
isSidebarOpen: state => state.activeId !== inactiveId, isSidebarOpen: state => state.activeId !== inactiveId,
isSwimlanesOn: state => { isSwimlanesOn: () => false,
if (!gon?.features?.boardsWithSwimlanes && !gon?.features?.swimlanes) {
return false;
}
return state.isShowingEpicsSwimlanes;
},
getIssueById: state => id => { getIssueById: state => id => {
return state.issues[id] || {}; return state.issues[id] || {};
}, },
......
...@@ -8,7 +8,6 @@ class Groups::BoardsController < Groups::ApplicationController ...@@ -8,7 +8,6 @@ class Groups::BoardsController < Groups::ApplicationController
before_action :assign_endpoint_vars before_action :assign_endpoint_vars
before_action do before_action do
push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false) push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false)
push_frontend_feature_flag(:boards_with_swimlanes, group, default_enabled: true)
end end
feature_category :boards feature_category :boards
......
...@@ -7,9 +7,6 @@ class Projects::BoardsController < Projects::ApplicationController ...@@ -7,9 +7,6 @@ class Projects::BoardsController < Projects::ApplicationController
before_action :check_issues_available! before_action :check_issues_available!
before_action :authorize_read_board!, only: [:index, :show] before_action :authorize_read_board!, only: [:index, :show]
before_action :assign_endpoint_vars before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:boards_with_swimlanes, project, default_enabled: true)
end
feature_category :boards feature_category :boards
......
...@@ -17,23 +17,12 @@ ...@@ -17,23 +17,12 @@
= render 'shared/issuable/search_bar', type: :boards, board: board = render 'shared/issuable/search_bar', type: :boards, board: board
#board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" } #board-app.boards-app.position-relative{ "v-cloak" => "true", data: board_data, ":class" => "{ 'is-compact': detailIssueVisible }" }
- if Feature.enabled?(:boards_with_swimlanes, current_board_parent, default_enabled: true) || Feature.enabled?(:graphql_board_lists, current_board_parent) %board-content{ "v-cloak" => "true",
%board-content{ "v-cloak" => "true", "ref" => "board_content",
"ref" => "board_content", ":lists" => "state.lists",
":lists" => "state.lists", ":can-admin-list" => can_admin_list,
":can-admin-list" => can_admin_list, ":disabled" => "disabled",
":disabled" => "disabled" } data: { qa_selector: "boards_list" } }
- else
.boards-list.w-100.py-3.px-2.text-nowrap{ data: { qa_selector: "boards_list" } }
.boards-app-loading.w-100.text-center{ "v-if" => "loading" }
= loading_icon(css_class: 'gl-mb-3')
%board{ "v-cloak" => "true",
"v-for" => "list in state.lists",
"ref" => "board",
":can-admin-list" => can_admin_list,
":list" => "list",
":disabled" => "disabled",
":key" => "list.id" }
= render "shared/boards/components/sidebar", group: group = render "shared/boards/components/sidebar", group: group
%board-settings-sidebar{ ":can-admin-list" => can_admin_list } %board-settings-sidebar{ ":can-admin-list" => can_admin_list }
- if @project - if @project
......
...@@ -182,7 +182,7 @@ ...@@ -182,7 +182,7 @@
= render 'shared/issuable/board_create_list_dropdown', board: board = render 'shared/issuable/board_create_list_dropdown', board: board
- if @project - if @project
#js-add-issues-btn.gl-ml-3{ data: { can_admin_list: can?(current_user, :admin_list, @project) } } #js-add-issues-btn.gl-ml-3{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
- if current_user && Feature.enabled?(:boards_with_swimlanes, @group, default_enabled: true) - if current_user
#js-board-epics-swimlanes-toggle #js-board-epics-swimlanes-toggle
#js-toggle-focus-btn #js-toggle-focus-btn
- elsif is_not_boards_modal_or_productivity_analytics && show_sorting_dropdown - elsif is_not_boards_modal_or_productivity_analytics && show_sorting_dropdown
......
---
name: boards_with_swimlanes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/218040
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/238222
milestone: 13.6
group: group::product planning
type: development
default_enabled: true
...@@ -3,6 +3,9 @@ import gettersCE from '~/boards/stores/getters'; ...@@ -3,6 +3,9 @@ import gettersCE from '~/boards/stores/getters';
export default { export default {
...gettersCE, ...gettersCE,
isSwimlanesOn: state => {
return Boolean(gon?.features?.swimlanes && state.isShowingEpicsSwimlanes);
},
getIssuesByEpic: (state, getters) => (listId, epicId) => { getIssuesByEpic: (state, getters) => (listId, epicId) => {
return getters.getIssuesByList(listId).filter(issue => issue.epic && issue.epic.id === epicId); return getters.getIssuesByList(listId).filter(issue => issue.epic && issue.epic.id === epicId);
}, },
...@@ -16,9 +19,6 @@ export default { ...@@ -16,9 +19,6 @@ export default {
}, },
shouldUseGraphQL: state => { shouldUseGraphQL: state => {
return ( return state.isShowingEpicsSwimlanes || gon?.features?.graphqlBoardLists;
(gon?.features?.boardsWithSwimlanes && state.isShowingEpicsSwimlanes) ||
gon?.features?.graphqlBoardLists
);
}, },
}; };
...@@ -16,7 +16,6 @@ module Resolvers ...@@ -16,7 +16,6 @@ module Resolvers
def resolve(**args) def resolve(**args)
return Epic.none unless board.present? return Epic.none unless board.present?
return Epic.none unless group.present? return Epic.none unless group.present?
return unless ::Feature.enabled?(:boards_with_swimlanes, group, default_enabled: true)
context.scoped_set!(:board, board) context.scoped_set!(:board, board)
......
...@@ -75,10 +75,9 @@ RSpec.describe 'issue boards', :js do ...@@ -75,10 +75,9 @@ RSpec.describe 'issue boards', :js do
end end
context 'swimlanes dropdown' do context 'swimlanes dropdown' do
context 'feature flag on' do context 'license feature on' do
before do before do
stub_licensed_features(swimlanes: true) stub_licensed_features(swimlanes: true)
stub_feature_flags(boards_with_swimlanes: true, swimlanes: true)
end end
it 'does not show Group by dropdown when user is not logged in' do it 'does not show Group by dropdown when user is not logged in' do
...@@ -96,9 +95,9 @@ RSpec.describe 'issue boards', :js do ...@@ -96,9 +95,9 @@ RSpec.describe 'issue boards', :js do
end end
end end
context 'feature flag off' do context 'license feature off' do
before do before do
stub_feature_flags(boards_with_swimlanes: false, swimlanes: false) stub_licensed_features(swimlanes: false)
end end
it 'does not show Group by dropdown when user is not logged in' do it 'does not show Group by dropdown when user is not logged in' do
......
...@@ -30,7 +30,6 @@ RSpec.describe 'epics swimlanes', :js do ...@@ -30,7 +30,6 @@ RSpec.describe 'epics swimlanes', :js do
project.add_maintainer(user) project.add_maintainer(user)
group.add_maintainer(user) group.add_maintainer(user)
stub_licensed_features(epics: true, swimlanes: true) stub_licensed_features(epics: true, swimlanes: true)
stub_feature_flags(epics: true, boards_with_swimlanes: true, swimlanes: true)
sign_in(user) sign_in(user)
visit_board_page visit_board_page
......
...@@ -23,7 +23,7 @@ RSpec.describe 'epics swimlanes', :js do ...@@ -23,7 +23,7 @@ RSpec.describe 'epics swimlanes', :js do
context 'link to swimlanes view' do context 'link to swimlanes view' do
before do before do
stub_licensed_features(epics: true) stub_licensed_features(epics: true, swimlanes: true)
sign_in(user) sign_in(user)
visit_epics_swimlanes_page visit_epics_swimlanes_page
end end
......
...@@ -42,7 +42,7 @@ describe('ee/BoardContent', () => { ...@@ -42,7 +42,7 @@ describe('ee/BoardContent', () => {
${false} | ${{ isShowingEpicsSwimlanes: false }} | ${false} ${false} | ${{ isShowingEpicsSwimlanes: false }} | ${false}
`('with featureFlag=$featureFlag and state=$state', ({ featureFlag, state, result }) => { `('with featureFlag=$featureFlag and state=$state', ({ featureFlag, state, result }) => {
beforeEach(() => { beforeEach(() => {
gon.features.boardsWithSwimlanes = featureFlag; gon.features.swimlanes = featureFlag;
Object.assign(store.state, state); Object.assign(store.state, state);
createComponent(); createComponent();
}); });
......
...@@ -12,7 +12,6 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -12,7 +12,6 @@ import axios from '~/lib/utils/axios_utils';
describe('BoardListSelector', () => { describe('BoardListSelector', () => {
global.gon.features = { global.gon.features = {
...(global.gon.features || {}), ...(global.gon.features || {}),
boardsWithSwimlanes: false,
graphqlBoardLists: false, graphqlBoardLists: false,
}; };
......
...@@ -16,6 +16,60 @@ describe('EE Boards Store Getters', () => { ...@@ -16,6 +16,60 @@ describe('EE Boards Store Getters', () => {
issues, issues,
}; };
describe('isSwimlanesOn', () => {
afterEach(() => {
window.gon = { features: {} };
});
describe('when swimlanes feature is true', () => {
beforeEach(() => {
window.gon = { features: { swimlanes: true } };
});
describe('when isShowingEpicsSwimlanes is true', () => {
it('returns true', () => {
const state = {
isShowingEpicsSwimlanes: true,
};
expect(getters.isSwimlanesOn(state)).toBe(true);
});
});
describe('when isShowingEpicsSwimlanes is false', () => {
it('returns false', () => {
const state = {
isShowingEpicsSwimlanes: false,
};
expect(getters.isSwimlanesOn(state)).toBe(false);
});
});
});
describe('when swimlanes feature is false', () => {
describe('when isShowingEpicsSwimlanes is true', () => {
it('returns false', () => {
const state = {
isShowingEpicsSwimlanes: true,
};
expect(getters.isSwimlanesOn(state)).toBe(false);
});
});
describe('when isShowingEpicsSwimlanes is false', () => {
it('returns false', () => {
const state = {
isShowingEpicsSwimlanes: false,
};
expect(getters.isSwimlanesOn(state)).toBe(false);
});
});
});
});
describe('getIssuesByEpic', () => { describe('getIssuesByEpic', () => {
it('returns issues for a given listId and epicId', () => { it('returns issues for a given listId and epicId', () => {
const getIssuesByList = () => mockIssues; const getIssuesByList = () => mockIssues;
......
...@@ -50,18 +50,6 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do ...@@ -50,18 +50,6 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do
end end
end end
context 'when boards_with_swimlanes is disabled' do
before do
stub_feature_flags(boards_with_swimlanes: false)
end
it 'returns nil' do
result = resolve_board_epics(board)
expect(result).to be_nil
end
end
context 'when user can access the group' do context 'when user can access the group' do
before do before do
group.add_developer(current_user) group.add_developer(current_user)
......
...@@ -184,7 +184,6 @@ describe('List model', () => { ...@@ -184,7 +184,6 @@ describe('List model', () => {
}), }),
); );
list.issues = []; list.issues = [];
global.gon.features = { boardsWithSwimlanes: false };
}); });
it('adds new issue to top of list', done => { it('adds new issue to top of list', done => {
......
...@@ -51,52 +51,8 @@ describe('Boards - Getters', () => { ...@@ -51,52 +51,8 @@ describe('Boards - Getters', () => {
window.gon = { features: {} }; window.gon = { features: {} };
}); });
describe('when boardsWithSwimlanes is true', () => { it('returns false', () => {
beforeEach(() => { expect(getters.isSwimlanesOn()).toBe(false);
window.gon = { features: { boardsWithSwimlanes: true } };
});
describe('when isShowingEpicsSwimlanes is true', () => {
it('returns true', () => {
const state = {
isShowingEpicsSwimlanes: true,
};
expect(getters.isSwimlanesOn(state)).toBe(true);
});
});
describe('when isShowingEpicsSwimlanes is false', () => {
it('returns false', () => {
const state = {
isShowingEpicsSwimlanes: false,
};
expect(getters.isSwimlanesOn(state)).toBe(false);
});
});
});
describe('when boardsWithSwimlanes is false', () => {
describe('when isShowingEpicsSwimlanes is true', () => {
it('returns false', () => {
const state = {
isShowingEpicsSwimlanes: true,
};
expect(getters.isSwimlanesOn(state)).toBe(false);
});
});
describe('when isShowingEpicsSwimlanes is false', () => {
it('returns false', () => {
const state = {
isShowingEpicsSwimlanes: false,
};
expect(getters.isSwimlanesOn(state)).toBe(false);
});
});
}); });
}); });
......
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