Commit e33b8d8f authored by Simon Knox's avatar Simon Knox Committed by Phil Hughes

Move remove column button to sidebar

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