Commit 494c4c50 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'psi-board-remove-list-to-sidebar' into 'master'

Move remove column button to sidebar RUN AS-IF-FOSS

See merge request gitlab-org/gitlab!44380
parents e8817635 e33b8d8f
<script>
import { mapGetters, mapActions } from 'vuex';
import Sortable from 'sortablejs';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
import EmptyComponent from '~/vue_shared/components/empty_component';
......@@ -22,7 +21,7 @@ export default {
directives: {
Tooltip,
},
mixins: [isWipLimitsOn, glFeatureFlagMixin()],
mixins: [glFeatureFlagMixin()],
props: {
list: {
type: Object,
......
import Vue from 'vue';
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import { hide } from '~/tooltips';
export default Vue.extend({
components: {
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
list: {
type: Object,
default: () => ({}),
required: false,
},
},
methods: {
deleteBoard() {
hide(this.$el);
// eslint-disable-next-line no-alert
if (window.confirm(__('Are you sure you want to delete this list?'))) {
this.list.destroy();
}
},
},
});
<script>
import { mapActions } from 'vuex';
import { mapActions, mapState } from 'vuex';
import {
GlButton,
GlButtonGroup,
......@@ -9,20 +9,18 @@ import {
GlSprintf,
GlTooltipDirective,
} from '@gitlab/ui';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import { n__, s__ } from '~/locale';
import AccessorUtilities from '../../lib/utils/accessor';
import BoardDelete from './board_delete';
import IssueCount from './issue_count.vue';
import boardsStore from '../stores/boards_store';
import eventHub from '../eventhub';
import { ListType } from '../constants';
import sidebarEventHub from '~/sidebar/event_hub';
import { inactiveId, LIST, ListType } from '../constants';
import { isScopedLabel } from '~/lib/utils/common_utils';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
BoardDelete,
GlButtonGroup,
GlButton,
GlLabel,
......@@ -34,7 +32,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [isWipLimitsOn, glFeatureFlagMixin()],
mixins: [glFeatureFlagMixin()],
props: {
list: {
type: Object,
......@@ -45,11 +43,6 @@ export default {
type: Boolean,
required: true,
},
canAdminList: {
type: Boolean,
required: false,
default: false,
},
isSwimlanesHeader: {
type: Boolean,
required: false,
......@@ -67,6 +60,7 @@ export default {
};
},
computed: {
...mapState(['activeId']),
isLoggedIn() {
return Boolean(gon.current_user_id);
},
......@@ -114,10 +108,7 @@ export default {
},
isSettingsShown() {
return (
this.listType !== ListType.backlog &&
this.showListHeaderButton &&
this.list.isExpanded &&
this.isWipLimitsOn
this.listType !== ListType.backlog && this.showListHeaderButton && this.list.isExpanded
);
},
showBoardListAndBoardInfo() {
......@@ -135,7 +126,14 @@ export default {
},
},
methods: {
...mapActions(['updateList']),
...mapActions(['updateList', 'setActiveId']),
openSidebarSettings() {
if (this.activeId === inactiveId) {
sidebarEventHub.$emit('sidebar.closeAll');
}
this.setActiveId({ id: this.list.id, sidebarType: LIST });
},
showScopedLabels(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
......@@ -278,22 +276,6 @@ export default {
</div>
</gl-tooltip>
<board-delete
v-if="canAdminList && !list.preset && list.id"
:list="list"
inline-template="true"
>
<gl-button
v-gl-tooltip.hover.bottom
:class="{ 'gl-display-none': !list.isExpanded }"
:aria-label="__('Delete list')"
class="board-delete no-drag gl-pr-0 gl-shadow-none! gl-mr-3"
:title="__('Delete list')"
icon="remove"
size="small"
@click.stop="deleteBoard"
/>
</board-delete>
<div
v-if="showBoardListAndBoardInfo"
class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag text-secondary"
......
<script>
import { GlDrawer, GlLabel } from '@gitlab/ui';
import { GlButton, GlDrawer, GlLabel } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import { __ } from '~/locale';
import boardsStore from '~/boards/stores/boards_store';
......@@ -17,6 +17,7 @@ export default {
label: 'label',
labelListText: __('Label'),
components: {
GlButton,
GlDrawer,
GlLabel,
BoardSettingsSidebarWipLimit: () =>
......@@ -25,6 +26,13 @@ export default {
import('ee_component/boards/components/board_settings_list_types.vue'),
},
mixins: [glFeatureFlagMixin()],
props: {
canAdminList: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
...mapGetters(['isSidebarOpen']),
...mapState(['activeId', 'sidebarType', 'boardLists']),
......@@ -62,6 +70,13 @@ export default {
showScopedLabels(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
deleteBoard() {
// eslint-disable-next-line no-alert
if (window.confirm(__('Are you sure you want to delete this list?'))) {
this.activeList.destroy();
this.unsetActiveId();
}
},
},
};
</script>
......@@ -91,6 +106,16 @@ export default {
:board-list-type="boardListType"
/>
<board-settings-sidebar-wip-limit :max-issue-count="activeList.maxIssueCount" />
<div v-if="canAdminList && !activeList.preset && activeList.id" class="gl-m-4">
<gl-button
variant="danger"
category="secondary"
icon="remove"
data-testid="remove-list"
@click.stop="deleteBoard"
>{{ __('Remove list') }}
</gl-button>
</div>
</template>
</gl-drawer>
</template>
export default {
computed: {
isWipLimitsOn() {
return false;
},
},
};
......@@ -36,7 +36,7 @@
":disabled" => "disabled",
":key" => "list.id" }
= render "shared/boards/components/sidebar", group: group
= render_if_exists 'shared/boards/components/board_settings_sidebar'
%board-settings-sidebar{ ":can-admin-list" => can_admin_list }
- if @project
%board-add-issues-modal{ "new-issue-path" => new_project_issue_path(@project),
"milestone-path" => milestones_filter_dropdown_path,
......
---
title: Move remove board column button to sidebar
merge_request: 44380
author:
type: changed
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { mapState, mapGetters } from 'vuex';
import BoardListHeaderFoss from '~/boards/components/board_list_header.vue';
import { __, sprintf, s__ } from '~/locale';
import boardsStore from '~/boards/stores/boards_store';
import { inactiveId, LIST } from '~/boards/constants';
import eventHub from '~/sidebar/event_hub';
export default {
extends: BoardListHeaderFoss,
......@@ -14,7 +12,7 @@ export default {
};
},
computed: {
...mapState(['activeId', 'issuesByListId']),
...mapState(['issuesByListId']),
...mapGetters(['isSwimlanesOn']),
issuesTooltip() {
const { maxIssueCount } = this.list;
......@@ -39,15 +37,5 @@ export default {
return null;
},
},
methods: {
...mapActions(['setActiveId']),
openSidebarSettings() {
if (this.activeId === inactiveId) {
eventHub.$emit('sidebar.closeAll');
}
this.setActiveId({ id: this.list.id, sidebarType: LIST });
},
},
};
</script>
export default {
computed: {
isWipLimitsOn() {
return Boolean(gon?.features?.wipLimits);
},
},
};
- if current_board_parent.feature_available?(:wip_limits)
%board-settings-sidebar
......@@ -87,79 +87,55 @@ describe('Board List Header Component', () => {
const findSettingsButton = () => wrapper.find({ ref: 'settingsBtn' });
describe('Settings Button', () => {
it.each(Object.values(ListType))(
'when feature flag is off: does not render for List Type `%s`',
listType => {
window.gon = {
features: {
wipLimits: false,
},
};
createComponent({ listType });
expect(findSettingsButton().exists()).toBe(false);
},
);
describe('when feature flag is on', () => {
const hasSettings = [ListType.assignee, ListType.milestone, ListType.label];
const hasNoSettings = [ListType.backlog, ListType.blank, ListType.closed, ListType.promotion];
const hasSettings = [ListType.assignee, ListType.milestone, ListType.label];
const hasNoSettings = [ListType.backlog, ListType.blank, ListType.closed, ListType.promotion];
beforeEach(() => {
window.gon = {
features: {
wipLimits: true,
},
};
});
it.each(hasSettings)('does render for List Type `%s`', listType => {
createComponent({ listType });
it.each(hasSettings)('does render for List Type `%s`', listType => {
createComponent({ listType });
expect(findSettingsButton().exists()).toBe(true);
});
expect(findSettingsButton().exists()).toBe(true);
});
it.each(hasNoSettings)('does not render for List Type `%s`', listType => {
createComponent({ listType });
it.each(hasNoSettings)('does not render for List Type `%s`', listType => {
createComponent({ listType });
expect(findSettingsButton().exists()).toBe(false);
});
expect(findSettingsButton().exists()).toBe(false);
it('has a test for each list type', () => {
Object.values(ListType).forEach(value => {
expect([...hasSettings, ...hasNoSettings]).toContain(value);
});
});
it('has a test for each list type', () => {
Object.values(ListType).forEach(value => {
expect([...hasSettings, ...hasNoSettings]).toContain(value);
});
describe('emits sidebar.closeAll event on openSidebarSettings', () => {
beforeEach(() => {
jest.spyOn(sidebarEventHub, '$emit');
});
describe('emits sidebar.closeAll event on openSidebarSettings', () => {
beforeEach(() => {
jest.spyOn(sidebarEventHub, '$emit');
});
it('emits event if no active List', () => {
// Shares the same behavior for any settings-enabled List type
createComponent({ listType: hasSettings[0] });
wrapper.vm.openSidebarSettings();
it('emits event if no active List', () => {
// Shares the same behavior for any settings-enabled List type
createComponent({ listType: hasSettings[0] });
wrapper.vm.openSidebarSettings();
expect(sidebarEventHub.$emit).toHaveBeenCalledWith('sidebar.closeAll');
});
expect(sidebarEventHub.$emit).toHaveBeenCalledWith('sidebar.closeAll');
});
it('does not emits event when there is an active List', () => {
store.state.activeId = listObj.id;
createComponent({ listType: hasSettings[0] });
wrapper.vm.openSidebarSettings();
it('does not emit event when there is an active List', () => {
store.state.activeId = listObj.id;
createComponent({ listType: hasSettings[0] });
wrapper.vm.openSidebarSettings();
expect(sidebarEventHub.$emit).not.toHaveBeenCalled();
});
expect(sidebarEventHub.$emit).not.toHaveBeenCalled();
});
});
});
describe('Swimlanes header', () => {
it('when collapsed, it displays info icon', () => {
createComponent({ isSwimlanesHeader: true, collapsed: true });
describe('Swimlanes header', () => {
it('when collapsed, it displays info icon', () => {
createComponent({ isSwimlanesHeader: true, collapsed: true });
expect(wrapper.find('.board-header-collapsed-info-icon').exists()).toBe(true);
});
expect(wrapper.find('.board-header-collapsed-info-icon').exists()).toBe(true);
});
});
});
......@@ -8499,9 +8499,6 @@ msgstr ""
msgid "Delete label: %{label_name} ?"
msgstr ""
msgid "Delete list"
msgstr ""
msgid "Delete pipeline"
msgstr ""
......@@ -21711,6 +21708,9 @@ msgstr ""
msgid "Remove limit"
msgstr ""
msgid "Remove list"
msgstr ""
msgid "Remove member"
msgstr ""
......
......@@ -103,7 +103,13 @@ RSpec.describe 'Issue Boards add issue modal', :js do
click_button 'Cancel'
end
accept_confirm { first('.board-delete').click }
page.within(find('.board:nth-child(2)')) do
find('button[title="List settings"]').click
end
page.within(find('.js-board-settings-sidebar')) do
accept_confirm { find('[data-testid="remove-list"]').click }
end
click_button('Add issues')
......
......@@ -159,9 +159,7 @@ RSpec.describe 'Issue Boards', :js do
end
it 'allows user to delete board' do
page.within(find('.board:nth-child(2)')) do
accept_confirm { find('.board-delete').click }
end
remove_list
wait_for_requests
......@@ -174,9 +172,7 @@ RSpec.describe 'Issue Boards', :js do
find('.js-new-board-list').click
page.within(find('.board:nth-child(2)')) do
accept_confirm { find('.board-delete').click }
end
remove_list
wait_for_requests
......@@ -670,4 +666,14 @@ RSpec.describe 'Issue Boards', :js do
click_button(link_text)
end
end
def remove_list
page.within(find('.board:nth-child(2)')) do
find('button[title="List settings"]').click
end
page.within(find('.js-board-settings-sidebar')) do
accept_confirm { find('[data-testid="remove-list"]').click }
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