Commit 20da9965 authored by Coung Ngo's avatar Coung Ngo

Hide labels from issue board cards

This commit creates a toggle button on the Issue Boards page which
shows and hides all labels on the page
parent 5e5924a2
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui'; import { GlTooltipDirective } from '@gitlab/ui';
import { sprintf, __ } from '~/locale'; import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -63,6 +64,7 @@ export default { ...@@ -63,6 +64,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(['isShowingLabels']),
numberOverLimit() { numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter; return this.issue.assignees.length - this.limitBeforeCounter;
}, },
...@@ -92,7 +94,7 @@ export default { ...@@ -92,7 +94,7 @@ export default {
return false; return false;
}, },
showLabelFooter() { showLabelFooter() {
return this.issue.labels.find(l => this.showLabel(l)) !== undefined; return this.isShowingLabels && this.issue.labels.find(this.showLabel);
}, },
issueReferencePath() { issueReferencePath() {
const { referencePath, groupId } = this.issue; const { referencePath, groupId } = this.issue;
......
...@@ -13,6 +13,7 @@ import 'ee_else_ce/boards/models/issue'; ...@@ -13,6 +13,7 @@ import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list'; import 'ee_else_ce/boards/models/list';
import '~/boards/models/milestone'; import '~/boards/models/milestone';
import '~/boards/models/project'; import '~/boards/models/project';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
import ModalStore from '~/boards/stores/modal_store'; import ModalStore from '~/boards/stores/modal_store';
import BoardService from 'ee_else_ce/boards/services/board_service'; import BoardService from 'ee_else_ce/boards/services/board_service';
...@@ -29,6 +30,7 @@ import { ...@@ -29,6 +30,7 @@ import {
} from '~/lib/utils/common_utils'; } from '~/lib/utils/common_utils';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle'; import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
import toggleFocusMode from 'ee_else_ce/boards/toggle_focus'; import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
import toggleLabels from 'ee_else_ce/boards/toggle_labels';
import { import {
setPromotionState, setPromotionState,
setWeigthFetchingState, setWeigthFetchingState,
...@@ -67,6 +69,7 @@ export default () => { ...@@ -67,6 +69,7 @@ export default () => {
BoardSidebar, BoardSidebar,
BoardAddIssuesModal, BoardAddIssuesModal,
}, },
store,
data: { data: {
state: boardsStore.state, state: boardsStore.state,
loading: true, loading: true,
...@@ -314,5 +317,6 @@ export default () => { ...@@ -314,5 +317,6 @@ export default () => {
} }
toggleFocusMode(ModalStore, boardsStore, $boardApp); toggleFocusMode(ModalStore, boardsStore, $boardApp);
toggleLabels();
mountMultipleBoardsSwitcher(); mountMultipleBoardsSwitcher();
}; };
export default {
getSnowplowLabelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
};
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import state from 'ee_else_ce/boards/stores/state'; import state from 'ee_else_ce/boards/stores/state';
import getters from 'ee_else_ce/boards/stores/getters';
import actions from 'ee_else_ce/boards/stores/actions'; import actions from 'ee_else_ce/boards/stores/actions';
import mutations from 'ee_else_ce/boards/stores/mutations'; import mutations from 'ee_else_ce/boards/stores/mutations';
Vue.use(Vuex); Vue.use(Vuex);
export default () => export const createStore = () =>
new Vuex.Store({ new Vuex.Store({
state, state,
getters,
actions, actions,
mutations, mutations,
}); });
export default createStore();
export default () => ({ export default () => ({
// ... isShowingLabels: true,
}); });
...@@ -545,3 +545,17 @@ ...@@ -545,3 +545,17 @@
.board-issue-path.js-show-tooltip { .board-issue-path.js-show-tooltip {
cursor: help; cursor: help;
} }
.boards-labels-toggle-wrapper {
display: flex;
align-items: center;
label {
font-weight: $gl-font-weight-normal;
margin-bottom: 0;
}
@include media-breakpoint-down(sm) {
margin-bottom: 10px;
}
}
...@@ -147,6 +147,7 @@ ...@@ -147,6 +147,7 @@
%button.clear-search.hidden{ type: 'button' } %button.clear-search.hidden{ type: 'button' }
= icon('times') = icon('times')
#boards-labels-toggle
.filter-dropdown-container.d-flex.flex-column.flex-md-row .filter-dropdown-container.d-flex.flex-column.flex-md-row
- if type == :boards - if type == :boards
.js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } } .js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } }
......
import actionsCE from '~/boards/stores/actions'; import actionsCE from '~/boards/stores/actions';
import * as types from './mutation_types';
const notImplemented = () => { const notImplemented = () => {
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
...@@ -8,6 +9,10 @@ const notImplemented = () => { ...@@ -8,6 +9,10 @@ const notImplemented = () => {
export default { export default {
...actionsCE, ...actionsCE,
toggleShowLabels({ commit }) {
commit(types.TOGGLE_SHOW_LABELS);
},
fetchAllBoards: () => { fetchAllBoards: () => {
notImplemented(); notImplemented();
}, },
......
import gettersCE from '~/boards/stores/getters';
export default {
...gettersCE,
};
...@@ -11,3 +11,4 @@ export const REQUEST_REMOVE_BOARD = 'REQUEST_REMOVE_BOARD'; ...@@ -11,3 +11,4 @@ export const REQUEST_REMOVE_BOARD = 'REQUEST_REMOVE_BOARD';
export const RECEIVE_REMOVE_BOARD_SUCCESS = 'RECEIVE_REMOVE_BOARD_SUCCESS'; export const RECEIVE_REMOVE_BOARD_SUCCESS = 'RECEIVE_REMOVE_BOARD_SUCCESS';
export const RECEIVE_REMOVE_BOARD_ERROR = 'RECEIVE_REMOVE_BOARD_ERROR'; export const RECEIVE_REMOVE_BOARD_ERROR = 'RECEIVE_REMOVE_BOARD_ERROR';
export const TOGGLE_PROMOTION_STATE = 'TOGGLE_PROMOTION_STATE'; export const TOGGLE_PROMOTION_STATE = 'TOGGLE_PROMOTION_STATE';
export const TOGGLE_SHOW_LABELS = 'TOGGLE_SHOW_LABELS';
...@@ -6,6 +6,12 @@ const notImplemented = () => { ...@@ -6,6 +6,12 @@ const notImplemented = () => {
}; };
export default { export default {
[mutationTypes.TOGGLE_SHOW_LABELS]: state => {
Object.assign(state, {
isShowingLabels: !state.isShowingLabels,
});
},
[mutationTypes.REQUEST_AVAILABLE_BOARDS]: () => { [mutationTypes.REQUEST_AVAILABLE_BOARDS]: () => {
notImplemented(); notImplemented();
}, },
......
import Vue from 'vue';
import { mapState, mapGetters, mapActions } from 'vuex';
import { GlToggle } from '@gitlab/ui';
import { __ } from '~/locale';
import Tracking from '~/tracking';
import store from '~/boards/stores';
export default () =>
new Vue({
el: document.getElementById('boards-labels-toggle'),
components: {
GlToggle,
},
store,
data: {
toggleLabel: __('Show labels'),
},
computed: {
...mapState(['isShowingLabels']),
...mapGetters(['getSnowplowLabelToggleState']),
},
methods: {
...mapActions(['toggleShowLabels']),
onToggle() {
this.toggleShowLabels();
Tracking.event(document.body.dataset.page, 'toggle', {
label: 'show_labels',
property: this.getSnowplowLabelToggleState,
});
},
},
template: `
<div class="boards-labels-toggle-wrapper prepend-left-10">
<span id="boards-labels-toggle-text">
{{toggleLabel}}
</span>
<gl-toggle
:value="isShowingLabels"
class="prepend-left-10"
label-on="Showing all labels"
label-off="Hiding all labels"
aria-describedby="boards-labels-toggle-text"
data-qa-selector="show_labels_toggle"
@change="onToggle"
/>
</div>
`,
});
---
title: Hide labels from issue board cards
merge_request: 18533
author:
type: added
...@@ -3,14 +3,16 @@ import ListLabel from '~/boards/models/label'; ...@@ -3,14 +3,16 @@ import ListLabel from '~/boards/models/label';
import IssueCardInner from '~/boards/components/issue_card_inner.vue'; import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import IssueCardWeight from 'ee/boards/components/issue_card_weight.vue'; import IssueCardWeight from 'ee/boards/components/issue_card_weight.vue';
import ListIssueEE from 'ee/boards/models/issue'; import ListIssueEE from 'ee/boards/models/issue';
import defaultStore from '~/boards/stores';
describe('Issue card component', () => { describe('Issue card component', () => {
let wrapper; let wrapper;
let issue; let issue;
let list; let list;
const createComponent = (props = {}) => { const createComponent = (props = {}, store = defaultStore) => {
wrapper = mount(IssueCardInner, { wrapper = mount(IssueCardInner, {
store,
propsData: { propsData: {
list, list,
issue, issue,
...@@ -87,6 +89,19 @@ describe('Issue card component', () => { ...@@ -87,6 +89,19 @@ describe('Issue card component', () => {
expect(wrapper.findAll('.badge').length).toBe(3); expect(wrapper.findAll('.badge').length).toBe(3);
expect(wrapper.text()).toContain(title); expect(wrapper.text()).toContain(title);
}); });
it('shows no labels when the isShowingLabels state is false', () => {
const store = {
...defaultStore,
state: {
...defaultStore.state,
isShowingLabels: false,
},
};
createComponent({}, store);
expect(wrapper.findAll('.board-card-labels').length).toBe(0);
});
}); });
describe('weight', () => { describe('weight', () => {
......
import actions from 'ee/boards/stores/actions'; import actions from 'ee/boards/stores/actions';
import * as types from 'ee/boards/stores/mutation_types';
import testAction from 'helpers/vuex_action_helper';
const expectNotImplemented = action => { const expectNotImplemented = action => {
it('is not implemented', () => { it('is not implemented', () => {
...@@ -6,6 +8,23 @@ const expectNotImplemented = action => { ...@@ -6,6 +8,23 @@ const expectNotImplemented = action => {
}); });
}; };
describe('toggleShowLabels', () => {
it('should commit mutation TOGGLE_SHOW_LABELS', done => {
const state = {
isShowingLabels: true,
};
testAction(
actions.toggleShowLabels,
null,
state,
[{ type: types.TOGGLE_SHOW_LABELS }],
[],
done,
);
});
});
describe('fetchAllBoards', () => { describe('fetchAllBoards', () => {
expectNotImplemented(actions.fetchAllBoards); expectNotImplemented(actions.fetchAllBoards);
}); });
......
...@@ -6,6 +6,28 @@ const expectNotImplemented = action => { ...@@ -6,6 +6,28 @@ const expectNotImplemented = action => {
}); });
}; };
describe('TOGGLE_SHOW_LABELS', () => {
it('toggles isShowingLabels from true to false', () => {
const state = {
isShowingLabels: true,
};
mutations.TOGGLE_SHOW_LABELS(state);
expect(state.isShowingLabels).toBe(false);
});
it('toggles isShowingLabels from false to true', () => {
const state = {
isShowingLabels: false,
};
mutations.TOGGLE_SHOW_LABELS(state);
expect(state.isShowingLabels).toBe(true);
});
});
describe('REQUEST_AVAILABLE_BOARDS', () => { describe('REQUEST_AVAILABLE_BOARDS', () => {
expectNotImplemented(mutations.REQUEST_AVAILABLE_BOARDS); expectNotImplemented(mutations.REQUEST_AVAILABLE_BOARDS);
}); });
......
...@@ -15050,6 +15050,9 @@ msgstr "" ...@@ -15050,6 +15050,9 @@ msgstr ""
msgid "Show file browser" msgid "Show file browser"
msgstr "" msgstr ""
msgid "Show labels"
msgstr ""
msgid "Show latest version" msgid "Show latest version"
msgstr "" msgstr ""
......
import getters from '~/boards/stores/getters';
describe('Boards - Getters', () => {
describe('getSnowplowLabelToggleState', () => {
it('should return "on" when isShowingLabels is true', () => {
const state = {
isShowingLabels: true,
};
expect(getters.getSnowplowLabelToggleState(state)).toBe('on');
});
it('should return "off" when isShowingLabels is false', () => {
const state = {
isShowingLabels: false,
};
expect(getters.getSnowplowLabelToggleState(state)).toBe('off');
});
});
});
...@@ -10,6 +10,7 @@ import eventHub from '~/boards/eventhub'; ...@@ -10,6 +10,7 @@ import eventHub from '~/boards/eventhub';
import '~/boards/models/label'; import '~/boards/models/label';
import '~/boards/models/assignee'; import '~/boards/models/assignee';
import '~/boards/models/list'; import '~/boards/models/list';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
import boardCard from '~/boards/components/board_card.vue'; import boardCard from '~/boards/components/board_card.vue';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
...@@ -40,6 +41,7 @@ describe('Board card', () => { ...@@ -40,6 +41,7 @@ describe('Board card', () => {
list.issues[0].labels.push(label1); list.issues[0].labels.push(label1);
vm = new BoardCardComp({ vm = new BoardCardComp({
store,
propsData: { propsData: {
list, list,
issue: list.issues[0], issue: list.issues[0],
......
...@@ -10,6 +10,7 @@ import BoardList from '~/boards/components/board_list.vue'; ...@@ -10,6 +10,7 @@ import BoardList from '~/boards/components/board_list.vue';
import '~/boards/models/issue'; import '~/boards/models/issue';
import '~/boards/models/list'; import '~/boards/models/list';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data'; import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
window.Sortable = Sortable; window.Sortable = Sortable;
...@@ -39,6 +40,7 @@ export default function createComponent({ done, listIssueProps = {}, componentPr ...@@ -39,6 +40,7 @@ export default function createComponent({ done, listIssueProps = {}, componentPr
const component = new BoardListComp({ const component = new BoardListComp({
el, el,
store,
propsData: { propsData: {
disabled: false, disabled: false,
list, list,
......
...@@ -10,6 +10,7 @@ import '~/boards/models/issue'; ...@@ -10,6 +10,7 @@ import '~/boards/models/issue';
import '~/boards/models/list'; import '~/boards/models/list';
import IssueCardInner from '~/boards/components/issue_card_inner.vue'; import IssueCardInner from '~/boards/components/issue_card_inner.vue';
import { listObj } from './mock_data'; import { listObj } from './mock_data';
import store from '~/boards/stores';
describe('Issue card component', () => { describe('Issue card component', () => {
const user = new ListAssignee({ const user = new ListAssignee({
...@@ -50,6 +51,7 @@ describe('Issue card component', () => { ...@@ -50,6 +51,7 @@ describe('Issue card component', () => {
component = new Vue({ component = new Vue({
el: document.querySelector('.test-container'), el: document.querySelector('.test-container'),
store,
components: { components: {
'issue-card': IssueCardInner, 'issue-card': IssueCardInner,
}, },
......
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