Commit 401b5524 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '332261-clean-up-the-vuex-store-from-labelsselect' into 'master'

Clean-up the Vuex store from labels widget

See merge request gitlab-org/gitlab!69944
parents 87e893d9 a1ed548c
...@@ -144,16 +144,11 @@ export default { ...@@ -144,16 +144,11 @@ export default {
v-if="glFeatures.labelsWidget" v-if="glFeatures.labelsWidget"
class="block labels js-labels-block" class="block labels js-labels-block"
:allow-label-remove="allowLabelEdit" :allow-label-remove="allowLabelEdit"
:allow-label-create="allowLabelCreate"
:allow-label-edit="allowLabelEdit"
:allow-multiselect="true" :allow-multiselect="true"
:allow-scoped-labels="allowScopedLabels"
:footer-create-label-title="__('Create project label')" :footer-create-label-title="__('Create project label')"
:footer-manage-label-title="__('Manage project labels')" :footer-manage-label-title="__('Manage project labels')"
:labels-create-title="__('Create project label')" :labels-create-title="__('Create project label')"
:labels-fetch-path="labelsFetchPath"
:labels-filter-base-path="projectIssuesPath" :labels-filter-base-path="projectIssuesPath"
:labels-manage-path="labelsManagePath"
:labels-select-in-progress="isLabelsSelectInProgress" :labels-select-in-progress="isLabelsSelectInProgress"
:selected-labels="selectedLabels" :selected-labels="selectedLabels"
:variant="$options.variant" :variant="$options.variant"
......
<script> <script>
import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui'; import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import DropdownContentsCreateView from './dropdown_contents_create_view.vue'; import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue'; import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue';
import { isDropdownVariantSidebar, isDropdownVariantEmbedded } from './utils';
export default { export default {
components: { components: {
...@@ -32,6 +32,10 @@ export default { ...@@ -32,6 +32,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
dropdownButtonText: {
type: String,
required: true,
},
footerCreateLabelTitle: { footerCreateLabelTitle: {
type: String, type: String,
required: true, required: true,
...@@ -40,10 +44,17 @@ export default { ...@@ -40,10 +44,17 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
variant: {
type: String,
required: true,
},
},
data() {
return {
showDropdownContentsCreateView: false,
};
}, },
computed: { computed: {
...mapState(['showDropdownContentsCreateView']),
...mapGetters(['dropdownButtonText', 'isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
dropdownContentsView() { dropdownContentsView() {
if (this.showDropdownContentsCreateView) { if (this.showDropdownContentsCreateView) {
return 'dropdown-contents-create-view'; return 'dropdown-contents-create-view';
...@@ -56,20 +67,25 @@ export default { ...@@ -56,20 +67,25 @@ export default {
showDropdownFooter() { showDropdownFooter() {
return ( return (
!this.showDropdownContentsCreateView && !this.showDropdownContentsCreateView &&
(this.isDropdownVariantSidebar || this.isDropdownVariantEmbedded) (this.isDropdownVariantSidebar(this.variant) ||
this.isDropdownVariantEmbedded(this.variant))
); );
}, },
}, },
methods: { methods: {
...mapActions(['toggleDropdownContentsCreateView']),
showDropdown() { showDropdown() {
this.$refs.dropdown.show(); this.$refs.dropdown.show();
}, },
toggleDropdownContentsCreateView() {
this.showDropdownContentsCreateView = !this.showDropdownContentsCreateView;
},
toggleDropdownContent() { toggleDropdownContent() {
this.toggleDropdownContentsCreateView(); this.toggleDropdownContentsCreateView();
// Required to recalculate dropdown position as its size changes // Required to recalculate dropdown position as its size changes
this.$refs.dropdown.$refs.dropdown.$_popper.scheduleUpdate(); this.$refs.dropdown.$refs.dropdown.$_popper.scheduleUpdate();
}, },
isDropdownVariantSidebar,
isDropdownVariantEmbedded,
}, },
}; };
</script> </script>
...@@ -83,7 +99,7 @@ export default { ...@@ -83,7 +99,7 @@ export default {
> >
<template #header> <template #header>
<div <div
v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded" v-if="isDropdownVariantSidebar(variant) || isDropdownVariantEmbedded(variant)"
class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!" class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
> >
<gl-button <gl-button
...@@ -123,7 +139,7 @@ export default { ...@@ -123,7 +139,7 @@ export default {
> >
{{ footerCreateLabelTitle }} {{ footerCreateLabelTitle }}
</gl-dropdown-item> </gl-dropdown-item>
<gl-dropdown-item :href="labelsManagePath"> <gl-dropdown-item :href="labelsManagePath" @click.native.capture.stop>
{{ footerManageLabelTitle }} {{ footerManageLabelTitle }}
</gl-dropdown-item> </gl-dropdown-item>
</div> </div>
......
...@@ -7,6 +7,7 @@ export default { ...@@ -7,6 +7,7 @@ export default {
components: { components: {
GlLabel, GlLabel,
}, },
inject: ['allowScopedLabels'],
props: { props: {
disableLabels: { disableLabels: {
type: Boolean, type: Boolean,
...@@ -21,10 +22,6 @@ export default { ...@@ -21,10 +22,6 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
allowScopedLabels: {
type: Boolean,
required: true,
},
labelsFilterBasePath: { labelsFilterBasePath: {
type: String, type: String,
required: true, required: true,
......
<script> <script>
import Vue from 'vue'; import Vue from 'vue';
import Vuex, { mapActions, mapGetters } from 'vuex'; import Vuex from 'vuex';
import { __ } from '~/locale'; import { __ } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import { DropdownVariant } from './constants'; import { DropdownVariant } from './constants';
...@@ -8,45 +8,33 @@ import DropdownContents from './dropdown_contents.vue'; ...@@ -8,45 +8,33 @@ import DropdownContents from './dropdown_contents.vue';
import DropdownValue from './dropdown_value.vue'; import DropdownValue from './dropdown_value.vue';
import DropdownValueCollapsed from './dropdown_value_collapsed.vue'; import DropdownValueCollapsed from './dropdown_value_collapsed.vue';
import issueLabelsQuery from './graphql/issue_labels.query.graphql'; import issueLabelsQuery from './graphql/issue_labels.query.graphql';
import labelsSelectModule from './store'; import {
isDropdownVariantSidebar,
isDropdownVariantStandalone,
isDropdownVariantEmbedded,
} from './utils';
Vue.use(Vuex); Vue.use(Vuex);
export default { export default {
store: new Vuex.Store(labelsSelectModule()),
components: { components: {
DropdownValue, DropdownValue,
DropdownContents, DropdownContents,
DropdownValueCollapsed, DropdownValueCollapsed,
SidebarEditableItem, SidebarEditableItem,
}, },
inject: ['iid', 'projectPath'], inject: ['iid', 'projectPath', 'allowLabelEdit'],
props: { props: {
allowLabelRemove: { allowLabelRemove: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false,
}, },
allowLabelEdit: {
type: Boolean,
required: false,
default: false,
},
allowLabelCreate: {
type: Boolean,
required: false,
default: false,
},
allowMultiselect: { allowMultiselect: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false,
}, },
allowScopedLabels: {
type: Boolean,
required: false,
default: false,
},
variant: { variant: {
type: String, type: String,
required: false, required: false,
...@@ -62,16 +50,6 @@ export default { ...@@ -62,16 +50,6 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
labelsFetchPath: {
type: String,
required: false,
default: '',
},
labelsManagePath: {
type: String,
required: false,
default: '',
},
labelsFilterBasePath: { labelsFilterBasePath: {
type: String, type: String,
required: false, required: false,
...@@ -133,46 +111,7 @@ export default { ...@@ -133,46 +111,7 @@ export default {
}, },
}, },
}, },
computed: {
...mapGetters([
'isDropdownVariantSidebar',
'isDropdownVariantStandalone',
'isDropdownVariantEmbedded',
]),
},
watch: {
selectedLabels(selectedLabels) {
this.setInitialState({
selectedLabels,
});
},
isEditing(newVal) {
if (newVal) {
this.toggleDropdownContents();
}
},
},
mounted() {
this.setInitialState({
variant: this.variant,
allowLabelRemove: this.allowLabelRemove,
allowLabelEdit: this.allowLabelEdit,
allowLabelCreate: this.allowLabelCreate,
allowMultiselect: this.allowMultiselect,
allowScopedLabels: this.allowScopedLabels,
dropdownButtonText: this.dropdownButtonText,
selectedLabels: this.selectedLabels,
labelsFetchPath: this.labelsFetchPath,
labelsManagePath: this.labelsManagePath,
labelsFilterBasePath: this.labelsFilterBasePath,
labelsFilterParam: this.labelsFilterParam,
labelsListTitle: this.labelsListTitle,
footerCreateLabelTitle: this.footerCreateLabelTitle,
footerManageLabelTitle: this.footerManageLabelTitle,
});
},
methods: { methods: {
...mapActions(['setInitialState']),
handleDropdownClose(labels) { handleDropdownClose(labels) {
if (labels.length) this.$emit('updateSelectedLabels', labels); if (labels.length) this.$emit('updateSelectedLabels', labels);
this.$emit('onDropdownClose'); this.$emit('onDropdownClose');
...@@ -188,6 +127,9 @@ export default { ...@@ -188,6 +127,9 @@ export default {
this.$refs.dropdownContents.showDropdown(); this.$refs.dropdownContents.showDropdown();
}); });
}, },
isDropdownVariantSidebar,
isDropdownVariantStandalone,
isDropdownVariantEmbedded,
}, },
}; };
</script> </script>
...@@ -196,11 +138,11 @@ export default { ...@@ -196,11 +138,11 @@ export default {
<div <div
class="labels-select-wrapper position-relative" class="labels-select-wrapper position-relative"
:class="{ :class="{
'is-standalone': isDropdownVariantStandalone, 'is-standalone': isDropdownVariantStandalone(variant),
'is-embedded': isDropdownVariantEmbedded, 'is-embedded': isDropdownVariantEmbedded(variant),
}" }"
> >
<template v-if="isDropdownVariantSidebar"> <template v-if="isDropdownVariantSidebar(variant)">
<dropdown-value-collapsed <dropdown-value-collapsed
ref="dropdownButtonCollapsed" ref="dropdownButtonCollapsed"
:labels="issueLabels" :labels="issueLabels"
...@@ -210,6 +152,7 @@ export default { ...@@ -210,6 +152,7 @@ export default {
ref="editable" ref="editable"
:title="__('Labels')" :title="__('Labels')"
:loading="labelsSelectInProgress" :loading="labelsSelectInProgress"
:can-edit="allowLabelEdit"
@open="showDropdown" @open="showDropdown"
> >
<template #collapsed> <template #collapsed>
...@@ -217,7 +160,6 @@ export default { ...@@ -217,7 +160,6 @@ export default {
:disable-labels="labelsSelectInProgress" :disable-labels="labelsSelectInProgress"
:selected-labels="issueLabels" :selected-labels="issueLabels"
:allow-label-remove="allowLabelRemove" :allow-label-remove="allowLabelRemove"
:allow-scoped-labels="allowScopedLabels"
:labels-filter-base-path="labelsFilterBasePath" :labels-filter-base-path="labelsFilterBasePath"
:labels-filter-param="labelsFilterParam" :labels-filter-param="labelsFilterParam"
@onLabelRemove="$emit('onLabelRemove', $event)" @onLabelRemove="$emit('onLabelRemove', $event)"
...@@ -230,7 +172,6 @@ export default { ...@@ -230,7 +172,6 @@ export default {
:disable-labels="labelsSelectInProgress" :disable-labels="labelsSelectInProgress"
:selected-labels="issueLabels" :selected-labels="issueLabels"
:allow-label-remove="allowLabelRemove" :allow-label-remove="allowLabelRemove"
:allow-scoped-labels="allowScopedLabels"
:labels-filter-base-path="labelsFilterBasePath" :labels-filter-base-path="labelsFilterBasePath"
:labels-filter-param="labelsFilterParam" :labels-filter-param="labelsFilterParam"
class="gl-mb-2" class="gl-mb-2"
...@@ -241,12 +182,14 @@ export default { ...@@ -241,12 +182,14 @@ export default {
<dropdown-contents <dropdown-contents
v-if="edit" v-if="edit"
ref="dropdownContents" ref="dropdownContents"
:dropdown-button-text="dropdownButtonText"
:allow-multiselect="allowMultiselect" :allow-multiselect="allowMultiselect"
:labels-list-title="labelsListTitle" :labels-list-title="labelsListTitle"
:footer-create-label-title="footerCreateLabelTitle" :footer-create-label-title="footerCreateLabelTitle"
:footer-manage-label-title="footerManageLabelTitle" :footer-manage-label-title="footerManageLabelTitle"
:labels-create-title="labelsCreateTitle" :labels-create-title="labelsCreateTitle"
:selected-labels="selectedLabels" :selected-labels="selectedLabels"
:variant="variant"
@closeDropdown="collapseDropdown" @closeDropdown="collapseDropdown"
@setLabels="handleDropdownClose" @setLabels="handleDropdownClose"
/> />
......
import * as types from './mutation_types';
export const setInitialState = ({ commit }, props) => commit(types.SET_INITIAL_STATE, props);
export const toggleDropdownButton = ({ commit }) => commit(types.TOGGLE_DROPDOWN_BUTTON);
export const toggleDropdownContents = ({ commit }) => commit(types.TOGGLE_DROPDOWN_CONTENTS);
export const toggleDropdownContentsCreateView = ({ commit }) =>
commit(types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW);
export const updateSelectedLabels = ({ commit }, labels) =>
commit(types.UPDATE_SELECTED_LABELS, { labels });
import { __, s__, sprintf } from '~/locale';
import { DropdownVariant } from '../constants';
/**
* Returns string representing current labels
* selection on dropdown button.
*
* @param {object} state
*/
export const dropdownButtonText = (state, getters) => {
const selectedLabels = getters.isDropdownVariantSidebar
? state.labels.filter((label) => label.set)
: state.selectedLabels;
if (!selectedLabels.length) {
return state.dropdownButtonText || __('Label');
} else if (selectedLabels.length > 1) {
return sprintf(s__('LabelSelect|%{firstLabelName} +%{remainingLabelCount} more'), {
firstLabelName: selectedLabels[0].title,
remainingLabelCount: selectedLabels.length - 1,
});
}
return selectedLabels[0].title;
};
/**
* Returns array containing only label IDs from
* selectedLabels array.
* @param {object} state
*/
export const selectedLabelsList = (state) => state.selectedLabels.map((label) => label.id);
/**
* Returns boolean representing whether dropdown variant
* is `sidebar`
* @param {object} state
*/
export const isDropdownVariantSidebar = (state) => state.variant === DropdownVariant.Sidebar;
/**
* Returns boolean representing whether dropdown variant
* is `standalone`
* @param {object} state
*/
export const isDropdownVariantStandalone = (state) => state.variant === DropdownVariant.Standalone;
/**
* Returns boolean representing whether dropdown variant
* is `embedded`
* @param {object} state
*/
export const isDropdownVariantEmbedded = (state) => state.variant === DropdownVariant.Embedded;
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
import state from './state';
export default () => ({
namespaced: true,
state: state(),
actions,
getters,
mutations,
});
export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
export const TOGGLE_DROPDOWN_BUTTON = 'TOGGLE_DROPDOWN_VISIBILITY';
export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS';
export const UPDATE_SELECTED_LABELS = 'UPDATE_SELECTED_LABELS';
export const TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW = 'TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW';
import { isScopedLabel, scopedLabelKey } from '~/lib/utils/common_utils';
import { DropdownVariant } from '../constants';
import * as types from './mutation_types';
export default {
[types.SET_INITIAL_STATE](state, props) {
Object.assign(state, { ...props });
},
[types.TOGGLE_DROPDOWN_BUTTON](state) {
state.showDropdownButton = !state.showDropdownButton;
},
[types.TOGGLE_DROPDOWN_CONTENTS](state) {
if (state.variant === DropdownVariant.Sidebar) {
state.showDropdownButton = !state.showDropdownButton;
}
state.showDropdownContents = !state.showDropdownContents;
// Ensure that Create View is hidden by default
// when dropdown contents are revealed.
if (state.showDropdownContents) {
state.showDropdownContentsCreateView = false;
}
},
[types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state) {
state.showDropdownContentsCreateView = !state.showDropdownContentsCreateView;
},
[types.UPDATE_SELECTED_LABELS](state, { labels }) {
// Find the label to update from all the labels
// and change `set` prop value to represent their current state.
const labelId = labels.pop()?.id;
const candidateLabel = state.labels.find((label) => labelId === label.id);
if (candidateLabel) {
candidateLabel.touched = true;
candidateLabel.set = !candidateLabel.set;
}
if (isScopedLabel(candidateLabel)) {
const scopedBase = scopedLabelKey(candidateLabel);
const currentActiveScopedLabel = state.labels.find(
({ title }) => title.indexOf(scopedBase) === 0 && title !== candidateLabel.title,
);
if (currentActiveScopedLabel) {
currentActiveScopedLabel.set = false;
}
}
},
};
export default () => ({
// Initial Data
labels: [],
selectedLabels: [],
labelsListTitle: '',
footerCreateLabelTitle: '',
footerManageLabelTitle: '',
dropdownButtonText: '',
// Paths
namespace: '',
labelsFetchPath: '',
labelsFilterBasePath: '',
// UI Flags
variant: '',
allowLabelRemove: false,
allowLabelCreate: false,
allowLabelEdit: false,
allowScopedLabels: false,
allowMultiselect: false,
showDropdownButton: false,
showDropdownContents: false,
showDropdownContentsCreateView: false,
labelsFetchInProgress: false,
labelCreateInProgress: false,
selectedLabelsUpdated: false,
});
import { DropdownVariant } from './constants';
/**
* Returns boolean representing whether dropdown variant
* is `sidebar`
* @param {string} variant
*/
export const isDropdownVariantSidebar = (variant) => variant === DropdownVariant.Sidebar;
/**
* Returns boolean representing whether dropdown variant
* is `standalone`
* @param {string} variant
*/
export const isDropdownVariantStandalone = (variant) => variant === DropdownVariant.Standalone;
/**
* Returns boolean representing whether dropdown variant
* is `embedded`
* @param {string} variant
*/
export const isDropdownVariantEmbedded = (variant) => variant === DropdownVariant.Embedded;
import { GlDropdown } from '@gitlab/ui'; import { GlDropdown } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants'; import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_widget/constants';
import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue'; import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue'; import DropdownContentsCreateView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue';
import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue'; import DropdownContentsLabelsView from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue';
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
import { mockConfig, mockLabels } from './mock_data'; import { mockLabels } from './mock_data';
Vue.use(Vuex);
describe('DropdownContent', () => { describe('DropdownContent', () => {
let wrapper; let wrapper;
const createComponent = ({ const createComponent = ({ props = {}, injected = {} } = {}) => {
initialState = mockConfig,
defaultProps = {},
injected = {},
} = {}) => {
const store = new Vuex.Store(labelsSelectModule());
store.dispatch('setInitialState', initialState);
wrapper = shallowMount(DropdownContents, { wrapper = shallowMount(DropdownContents, {
propsData: { propsData: {
...defaultProps,
labelsCreateTitle: 'test', labelsCreateTitle: 'test',
selectedLabels: mockLabels, selectedLabels: mockLabels,
allowMultiselect: true, allowMultiselect: true,
labelsListTitle: 'Assign labels', labelsListTitle: 'Assign labels',
footerCreateLabelTitle: 'create', footerCreateLabelTitle: 'create',
footerManageLabelTitle: 'manage', footerManageLabelTitle: 'manage',
dropdownButtonText: 'Labels',
variant: 'sidebar',
...props,
}, },
provide: { provide: {
allowLabelCreate: true, allowLabelCreate: true,
labelsManagePath: 'foo/bar', labelsManagePath: 'foo/bar',
...injected, ...injected,
}, },
store,
stubs: { stubs: {
GlDropdown, GlDropdown,
}, },
...@@ -61,7 +49,7 @@ describe('DropdownContent', () => { ...@@ -61,7 +49,7 @@ describe('DropdownContent', () => {
describe('Create view', () => { describe('Create view', () => {
beforeEach(() => { beforeEach(() => {
wrapper.vm.$store.dispatch('toggleDropdownContentsCreateView'); wrapper.vm.toggleDropdownContentsCreateView();
}); });
it('renders create view when `showDropdownContentsCreateView` prop is `true`', () => { it('renders create view when `showDropdownContentsCreateView` prop is `true`', () => {
...@@ -91,13 +79,13 @@ describe('DropdownContent', () => { ...@@ -91,13 +79,13 @@ describe('DropdownContent', () => {
}); });
it('does not render footer on standalone dropdown', () => { it('does not render footer on standalone dropdown', () => {
createComponent({ initialState: { ...mockConfig, variant: DropdownVariant.Standalone } }); createComponent({ props: { variant: DropdownVariant.Standalone } });
expect(findDropdownFooter().exists()).toBe(false); expect(findDropdownFooter().exists()).toBe(false);
}); });
it('renders footer on embedded dropdown', () => { it('renders footer on embedded dropdown', () => {
createComponent({ initialState: { ...mockConfig, variant: DropdownVariant.Embedded } }); createComponent({ props: { variant: DropdownVariant.Embedded } });
expect(findDropdownFooter().exists()).toBe(true); expect(findDropdownFooter().exists()).toBe(true);
}); });
......
...@@ -20,11 +20,13 @@ describe('DropdownValue', () => { ...@@ -20,11 +20,13 @@ describe('DropdownValue', () => {
propsData: { propsData: {
selectedLabels: [mockRegularLabel, mockScopedLabel], selectedLabels: [mockRegularLabel, mockScopedLabel],
allowLabelRemove: true, allowLabelRemove: true,
allowScopedLabels: true,
labelsFilterBasePath: '/gitlab-org/my-project/issues', labelsFilterBasePath: '/gitlab-org/my-project/issues',
labelsFilterParam: 'label_name', labelsFilterParam: 'label_name',
...props, ...props,
}, },
provide: {
allowScopedLabels: true,
},
}); });
}; };
......
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue'; import DropdownContents from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue';
import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue'; import DropdownValue from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue';
import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue'; import DropdownValueCollapsed from '~/vue_shared/components/sidebar/labels_select_widget/dropdown_value_collapsed.vue';
import LabelsSelectRoot from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue'; import LabelsSelectRoot from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_widget/store';
import { mockConfig } from './mock_data'; import { mockConfig } from './mock_data';
jest.mock('~/lib/utils/common_utils', () => ({
isInViewport: jest.fn().mockReturnValue(true),
}));
const localVue = createLocalVue();
localVue.use(Vuex);
describe('LabelsSelectRoot', () => { describe('LabelsSelectRoot', () => {
let wrapper; let wrapper;
let store;
const createComponent = (config = mockConfig, slots = {}) => { const createComponent = (config = mockConfig, slots = {}) => {
wrapper = shallowMount(LabelsSelectRoot, { wrapper = shallowMount(LabelsSelectRoot, {
localVue,
slots, slots,
store,
propsData: config, propsData: config,
stubs: { stubs: {
DropdownContents, DropdownContents,
...@@ -35,14 +22,11 @@ describe('LabelsSelectRoot', () => { ...@@ -35,14 +22,11 @@ describe('LabelsSelectRoot', () => {
iid: '1', iid: '1',
projectPath: 'test', projectPath: 'test',
canUpdate: true, canUpdate: true,
allowLabelEdit: true,
}, },
}); });
}; };
beforeEach(() => {
store = new Vuex.Store(labelsSelectModule());
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
......
...@@ -34,18 +34,12 @@ export const mockLabels = [ ...@@ -34,18 +34,12 @@ export const mockLabels = [
]; ];
export const mockConfig = { export const mockConfig = {
allowLabelEdit: true,
allowLabelCreate: true,
allowScopedLabels: true,
allowMultiselect: true, allowMultiselect: true,
labelsListTitle: 'Assign labels', labelsListTitle: 'Assign labels',
labelsCreateTitle: 'Create label', labelsCreateTitle: 'Create label',
variant: 'sidebar', variant: 'sidebar',
dropdownOnly: false,
selectedLabels: [mockRegularLabel, mockScopedLabel], selectedLabels: [mockRegularLabel, mockScopedLabel],
labelsSelectInProgress: false, labelsSelectInProgress: false,
labelsFetchPath: '/gitlab-org/my-project/-/labels.json',
labelsManagePath: '/gitlab-org/my-project/-/labels',
labelsFilterBasePath: '/gitlab-org/my-project/issues', labelsFilterBasePath: '/gitlab-org/my-project/issues',
labelsFilterParam: 'label_name', labelsFilterParam: 'label_name',
footerCreateLabelTitle: 'create', footerCreateLabelTitle: 'create',
......
import testAction from 'helpers/vuex_action_helper';
import * as actions from '~/vue_shared/components/sidebar/labels_select_widget/store/actions';
import * as types from '~/vue_shared/components/sidebar/labels_select_widget/store/mutation_types';
import defaultState from '~/vue_shared/components/sidebar/labels_select_widget/store/state';
jest.mock('~/flash');
describe('LabelsSelect Actions', () => {
let state;
const mockInitialState = {
labels: [],
selectedLabels: [],
};
beforeEach(() => {
state = { ...defaultState() };
});
describe('setInitialState', () => {
it('sets initial store state', (done) => {
testAction(
actions.setInitialState,
mockInitialState,
state,
[{ type: types.SET_INITIAL_STATE, payload: mockInitialState }],
[],
done,
);
});
});
describe('toggleDropdownButton', () => {
it('toggles dropdown button', (done) => {
testAction(
actions.toggleDropdownButton,
{},
state,
[{ type: types.TOGGLE_DROPDOWN_BUTTON }],
[],
done,
);
});
});
describe('toggleDropdownContents', () => {
it('toggles dropdown contents', (done) => {
testAction(
actions.toggleDropdownContents,
{},
state,
[{ type: types.TOGGLE_DROPDOWN_CONTENTS }],
[],
done,
);
});
});
describe('toggleDropdownContentsCreateView', () => {
it('toggles dropdown create view', (done) => {
testAction(
actions.toggleDropdownContentsCreateView,
{},
state,
[{ type: types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW }],
[],
done,
);
});
});
describe('updateSelectedLabels', () => {
it('updates `state.labels` based on provided `labels` param', (done) => {
const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
testAction(
actions.updateSelectedLabels,
labels,
state,
[{ type: types.UPDATE_SELECTED_LABELS, payload: { labels } }],
[],
done,
);
});
});
});
import * as getters from '~/vue_shared/components/sidebar/labels_select_widget/store/getters';
describe('LabelsSelect Getters', () => {
describe('dropdownButtonText', () => {
it.each`
labelType | dropdownButtonText | expected
${'default'} | ${''} | ${'Label'}
${'custom'} | ${'Custom label'} | ${'Custom label'}
`(
'returns $labelType text when state.labels has no selected labels',
({ dropdownButtonText, expected }) => {
const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
const selectedLabels = [];
const state = { labels, selectedLabels, dropdownButtonText };
expect(getters.dropdownButtonText(state, {})).toBe(expected);
},
);
it('returns label title when state.labels has only 1 label', () => {
const labels = [{ id: 1, title: 'Foobar', set: true }];
expect(getters.dropdownButtonText({ labels }, { isDropdownVariantSidebar: true })).toBe(
'Foobar',
);
});
it('returns first label title and remaining labels count when state.labels has more than 1 label', () => {
const labels = [
{ id: 1, title: 'Foo', set: true },
{ id: 2, title: 'Bar', set: true },
];
expect(getters.dropdownButtonText({ labels }, { isDropdownVariantSidebar: true })).toBe(
'Foo +1 more',
);
});
});
describe('selectedLabelsList', () => {
it('returns array of IDs of all labels within `state.selectedLabels`', () => {
const selectedLabels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
expect(getters.selectedLabelsList({ selectedLabels })).toEqual([1, 2, 3, 4]);
});
});
describe('isDropdownVariantSidebar', () => {
it('returns `true` when `state.variant` is "sidebar"', () => {
expect(getters.isDropdownVariantSidebar({ variant: 'sidebar' })).toBe(true);
});
});
describe('isDropdownVariantStandalone', () => {
it('returns `true` when `state.variant` is "standalone"', () => {
expect(getters.isDropdownVariantStandalone({ variant: 'standalone' })).toBe(true);
});
});
});
import * as types from '~/vue_shared/components/sidebar/labels_select_widget/store/mutation_types';
import mutations from '~/vue_shared/components/sidebar/labels_select_widget/store/mutations';
describe('LabelsSelect Mutations', () => {
describe(`${types.SET_INITIAL_STATE}`, () => {
it('initializes provided props to store state', () => {
const state = {};
mutations[types.SET_INITIAL_STATE](state, {
labels: 'foo',
});
expect(state.labels).toEqual('foo');
});
});
describe(`${types.TOGGLE_DROPDOWN_BUTTON}`, () => {
it('toggles value of `state.showDropdownButton`', () => {
const state = {
showDropdownButton: false,
};
mutations[types.TOGGLE_DROPDOWN_BUTTON](state);
expect(state.showDropdownButton).toBe(true);
});
});
describe(`${types.TOGGLE_DROPDOWN_CONTENTS}`, () => {
it('toggles value of `state.showDropdownButton` when `state.dropdownOnly` is false', () => {
const state = {
dropdownOnly: false,
showDropdownButton: false,
variant: 'sidebar',
};
mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
expect(state.showDropdownButton).toBe(true);
});
it('toggles value of `state.showDropdownContents`', () => {
const state = {
showDropdownContents: false,
};
mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
expect(state.showDropdownContents).toBe(true);
});
it('sets value of `state.showDropdownContentsCreateView` to `false` when `showDropdownContents` is true', () => {
const state = {
showDropdownContents: false,
showDropdownContentsCreateView: true,
};
mutations[types.TOGGLE_DROPDOWN_CONTENTS](state);
expect(state.showDropdownContentsCreateView).toBe(false);
});
});
describe(`${types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW}`, () => {
it('toggles value of `state.showDropdownContentsCreateView`', () => {
const state = {
showDropdownContentsCreateView: false,
};
mutations[types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state);
expect(state.showDropdownContentsCreateView).toBe(true);
});
});
describe(`${types.UPDATE_SELECTED_LABELS}`, () => {
let labels;
beforeEach(() => {
labels = [
{ id: 1, title: 'scoped::test', set: true },
{ id: 2, set: false, title: 'scoped::one' },
{ id: 3, title: '' },
{ id: 4, title: '' },
];
});
it('updates `state.labels` to include `touched` and `set` props based on provided `labels` param', () => {
const updatedLabelIds = [2];
const state = {
labels,
};
mutations[types.UPDATE_SELECTED_LABELS](state, { labels: [{ id: 2 }] });
state.labels.forEach((label) => {
if (updatedLabelIds.includes(label.id)) {
expect(label.touched).toBe(true);
expect(label.set).toBe(true);
}
});
});
describe('when label is scoped', () => {
it('unsets the currently selected scoped label and sets the current label', () => {
const state = {
labels,
};
mutations[types.UPDATE_SELECTED_LABELS](state, {
labels: [{ id: 2, title: 'scoped::one' }],
});
expect(state.labels).toEqual([
{ id: 1, title: 'scoped::test', set: false },
{ id: 2, set: true, title: 'scoped::one', touched: true },
{ id: 3, title: '' },
{ id: 4, title: '' },
]);
});
});
});
});
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