Commit c0b52867 authored by Coung Ngo's avatar Coung Ngo Committed by Kushal Pandya

Convert Issue sidebar labels to Vue

Issue sidebar labels was entirely in Haml. By converting it
to Vue, we can use gitlab-ui components
parent a90559bd
...@@ -4,8 +4,8 @@ import MilestoneSelect from './milestone_select'; ...@@ -4,8 +4,8 @@ import MilestoneSelect from './milestone_select';
import LabelsSelect from './labels_select'; import LabelsSelect from './labels_select';
import IssuableContext from './issuable_context'; import IssuableContext from './issuable_context';
import Sidebar from './right_sidebar'; import Sidebar from './right_sidebar';
import DueDateSelectors from './due_date_select'; import DueDateSelectors from './due_date_select';
import { mountSidebarLabels } from '~/sidebar/mount_sidebar';
export default () => { export default () => {
const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML); const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
...@@ -17,4 +17,6 @@ export default () => { ...@@ -17,4 +17,6 @@ export default () => {
new IssuableContext(sidebarOptions.currentUser); new IssuableContext(sidebarOptions.currentUser);
new DueDateSelectors(); new DueDateSelectors();
Sidebar.initialize(); Sidebar.initialize();
mountSidebarLabels();
}; };
<script>
import $ from 'jquery';
import { difference, union } from 'lodash';
import { mapState, mapActions } from 'vuex';
import flash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
export default {
components: {
LabelsSelect,
},
variant: DropdownVariant.Sidebar,
inject: [
'allowLabelCreate',
'allowLabelEdit',
'allowScopedLabels',
'iid',
'initiallySelectedLabels',
'issuableType',
'labelsFetchPath',
'labelsManagePath',
'labelsUpdatePath',
'projectIssuesPath',
'projectPath',
],
data: () => ({
labelsSelectInProgress: false,
}),
computed: {
...mapState(['selectedLabels']),
},
mounted() {
this.setInitialState({
selectedLabels: this.initiallySelectedLabels,
});
},
methods: {
...mapActions(['setInitialState', 'replaceSelectedLabels']),
handleDropdownClose() {
$(this.$el).trigger('hidden.gl.dropdown');
},
handleUpdateSelectedLabels(labels) {
const currentLabelIds = this.selectedLabels.map(label => label.id);
const userAddedLabelIds = labels.filter(label => label.set).map(label => label.id);
const userRemovedLabelIds = labels.filter(label => !label.set).map(label => label.id);
const issuableLabels = difference(
union(currentLabelIds, userAddedLabelIds),
userRemovedLabelIds,
);
this.labelsSelectInProgress = true;
axios({
data: {
[this.issuableType]: {
label_ids: issuableLabels,
},
},
method: 'put',
url: this.labelsUpdatePath,
})
.then(({ data }) => this.replaceSelectedLabels(data.labels))
.catch(() => flash(__('An error occurred while updating labels.')))
.finally(() => {
this.labelsSelectInProgress = false;
});
},
},
};
</script>
<template>
<labels-select
class="block labels js-labels-block"
:allow-label-create="allowLabelCreate"
:allow-label-edit="allowLabelEdit"
:allow-multiselect="true"
:allow-scoped-labels="allowScopedLabels"
:footer-create-label-title="__('Create project label')"
:footer-manage-label-title="__('Manage project labels')"
:labels-create-title="__('Create project label')"
:labels-fetch-path="labelsFetchPath"
:labels-filter-base-path="projectIssuesPath"
:labels-manage-path="labelsManagePath"
:labels-select-in-progress="labelsSelectInProgress"
:selected-labels="selectedLabels"
:variant="$options.sidebar"
@onDropdownClose="handleDropdownClose"
@updateSelectedLabels="handleUpdateSelectedLabels"
>
{{ __('None') }}
</labels-select>
</template>
import $ from 'jquery'; import $ from 'jquery';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue'; import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue';
import SidebarAssignees from './components/assignees/sidebar_assignees.vue'; import SidebarAssignees from './components/assignees/sidebar_assignees.vue';
import SidebarLabels from './components/labels/sidebar_labels.vue';
import ConfidentialIssueSidebar from './components/confidential/confidential_issue_sidebar.vue'; import ConfidentialIssueSidebar from './components/confidential/confidential_issue_sidebar.vue';
import SidebarMoveIssue from './lib/sidebar_move_issue'; import SidebarMoveIssue from './lib/sidebar_move_issue';
import IssuableLockForm from './components/lock/issuable_lock_form.vue'; import IssuableLockForm from './components/lock/issuable_lock_form.vue';
...@@ -12,11 +14,13 @@ import SidebarSeverity from './components/severity/sidebar_severity.vue'; ...@@ -12,11 +14,13 @@ import SidebarSeverity from './components/severity/sidebar_severity.vue';
import Translate from '../vue_shared/translate'; import Translate from '../vue_shared/translate';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { store } from '~/notes/stores'; import { store } from '~/notes/stores';
import { isInIssuePage } from '~/lib/utils/common_utils'; import { isInIssuePage, parseBoolean } from '~/lib/utils/common_utils';
import mergeRequestStore from '~/mr_notes/stores'; import mergeRequestStore from '~/mr_notes/stores';
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
Vue.use(Translate); Vue.use(Translate);
Vue.use(VueApollo); Vue.use(VueApollo);
Vue.use(Vuex);
function getSidebarOptions() { function getSidebarOptions() {
return JSON.parse(document.querySelector('.js-sidebar-options').innerHTML); return JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
...@@ -52,6 +56,29 @@ function mountAssigneesComponent(mediator) { ...@@ -52,6 +56,29 @@ function mountAssigneesComponent(mediator) {
}); });
} }
export function mountSidebarLabels() {
const el = document.querySelector('.js-sidebar-labels');
if (!el) {
return false;
}
const labelsStore = new Vuex.Store(labelsSelectModule());
return new Vue({
el,
provide: {
...el.dataset,
allowLabelCreate: parseBoolean(el.dataset.allowLabelCreate),
allowLabelEdit: parseBoolean(el.dataset.canEdit),
allowScopedLabels: parseBoolean(el.dataset.allowScopedLabels),
initiallySelectedLabels: JSON.parse(el.dataset.selectedLabels),
},
store: labelsStore,
render: createElement => createElement(SidebarLabels),
});
}
function mountConfidentialComponent(mediator) { function mountConfidentialComponent(mediator) {
const el = document.getElementById('js-confidential-entry-point'); const el = document.getElementById('js-confidential-entry-point');
......
...@@ -252,10 +252,10 @@ export default { ...@@ -252,10 +252,10 @@ export default {
:allow-label-edit="allowLabelEdit" :allow-label-edit="allowLabelEdit"
:labels-select-in-progress="labelsSelectInProgress" :labels-select-in-progress="labelsSelectInProgress"
/> />
<dropdown-value v-show="!showDropdownButton"> <dropdown-value>
<slot></slot> <slot></slot>
</dropdown-value> </dropdown-value>
<dropdown-button v-show="dropdownButtonVisible" /> <dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" />
<dropdown-contents <dropdown-contents
v-if="dropdownButtonVisible && showDropdownContents" v-if="dropdownButtonVisible && showDropdownContents"
ref="dropdownContents" ref="dropdownContents"
......
...@@ -54,5 +54,8 @@ export const createLabel = ({ state, dispatch }, label) => { ...@@ -54,5 +54,8 @@ export const createLabel = ({ state, dispatch }, label) => {
}); });
}; };
export const replaceSelectedLabels = ({ commit }, selectedLabels) =>
commit(types.REPLACE_SELECTED_LABELS, selectedLabels);
export const updateSelectedLabels = ({ commit }, labels) => export const updateSelectedLabels = ({ commit }, labels) =>
commit(types.UPDATE_SELECTED_LABELS, { labels }); commit(types.UPDATE_SELECTED_LABELS, { labels });
...@@ -15,6 +15,7 @@ export const RECEIVE_CREATE_LABEL_FAILURE = 'RECEIVE_CREATE_LABEL_FAILURE'; ...@@ -15,6 +15,7 @@ export const RECEIVE_CREATE_LABEL_FAILURE = 'RECEIVE_CREATE_LABEL_FAILURE';
export const TOGGLE_DROPDOWN_BUTTON = 'TOGGLE_DROPDOWN_VISIBILITY'; export const TOGGLE_DROPDOWN_BUTTON = 'TOGGLE_DROPDOWN_VISIBILITY';
export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS'; export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS';
export const REPLACE_SELECTED_LABELS = 'REPLACE_SELECTED_LABELS';
export const UPDATE_SELECTED_LABELS = 'UPDATE_SELECTED_LABELS'; export const UPDATE_SELECTED_LABELS = 'UPDATE_SELECTED_LABELS';
export const TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW = 'TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW'; export const TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW = 'TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW';
...@@ -57,6 +57,10 @@ export default { ...@@ -57,6 +57,10 @@ export default {
state.labelCreateInProgress = false; state.labelCreateInProgress = false;
}, },
[types.REPLACE_SELECTED_LABELS](state, selectedLabels = []) {
state.selectedLabels = selectedLabels;
},
[types.UPDATE_SELECTED_LABELS](state, { labels }) { [types.UPDATE_SELECTED_LABELS](state, { labels }) {
// Find the label to update from all the labels // Find the label to update from all the labels
// and change `set` prop value to represent their current state. // and change `set` prop value to represent their current state.
......
...@@ -145,6 +145,13 @@ ...@@ -145,6 +145,13 @@
.value.dont-hide ~ .selectbox { .value.dont-hide ~ .selectbox {
padding-top: $gl-padding-8; padding-top: $gl-padding-8;
} }
// This is for sidebar components using gl-button for the Edit button to be consistent with the
// rest of the sidebar, and could be removed once the sidebar has been fully converted to use
// gitlab-ui components.
.title .gl-button {
color: $gl-text-color;
}
} }
.pikaday-container { .pikaday-container {
......
...@@ -45,6 +45,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -45,6 +45,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:tribute_autocomplete, @project) push_frontend_feature_flag(:tribute_autocomplete, @project)
push_frontend_feature_flag(:vue_issuables_list, project) push_frontend_feature_flag(:vue_issuables_list, project)
push_frontend_feature_flag(:design_management_todo_button, project, default_enabled: true) push_frontend_feature_flag(:design_management_todo_button, project, default_enabled: true)
push_frontend_feature_flag(:vue_sidebar_labels, @project)
end end
before_action only: :show do before_action only: :show do
......
...@@ -101,36 +101,50 @@ ...@@ -101,36 +101,50 @@
= dropdown_content do = dropdown_content do
.js-due-date-calendar .js-due-date-calendar
- selected_labels = issuable_sidebar[:labels]
.block.labels - if Feature.enabled?(:vue_sidebar_labels, @project)
.sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(selected_labels), data: { placement: "left", container: "body", boundary: 'viewport' } } .js-sidebar-labels{ data: { allow_label_create: issuable_sidebar.dig(:current_user, :can_admin_label).to_s,
= sprite_icon('labels') allow_scoped_labels: issuable_sidebar[:scoped_labels_available].to_s,
%span can_edit: can_edit_issuable.to_s,
= selected_labels.size iid: issuable_sidebar[:iid],
.title.hide-collapsed issuable_type: issuable_type,
= _('Labels') labels_fetch_path: issuable_sidebar[:project_labels_path],
= loading_icon(css_class: 'gl-vertical-align-text-bottom hidden block-loading') labels_manage_path: project_labels_path(@project),
- if can_edit_issuable labels_update_path: issuable_sidebar[:issuable_json_path],
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { qa_selector: "edit_labels_link", track_label: "right_sidebar", track_property: "labels", track_event: "click_edit_button", track_value: "" } project_issues_path: issuable_sidebar[:project_issuables_path],
.value.issuable-show-labels.dont-hide.hide-collapsed{ class: ("has-labels" if selected_labels.any?), data: { qa_selector: 'labels_block' } } project_path: @project.full_path,
- if selected_labels.any? selected_labels: issuable_sidebar[:labels].to_json } }
- selected_labels.each do |label_hash| - else
= render_label(label_from_hash(label_hash).present(issuable_subject: nil), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title]), dataset: { qa_selector: 'label', qa_label_name: label_hash[:title] }) - selected_labels = issuable_sidebar[:labels]
- else .block.labels
%span.no-value .sidebar-collapsed-icon.js-sidebar-labels-tooltip{ title: issuable_labels_tooltip(selected_labels), data: { placement: "left", container: "body", boundary: 'viewport' } }
= _('None') = sprite_icon('labels')
.selectbox.hide-collapsed %span
- selected_labels.each do |label| = selected_labels.size
= hidden_field_tag "#{issuable_type}[label_names][]", label[:id], id: nil .title.hide-collapsed
.dropdown = _('Labels')
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: sidebar_label_dropdown_data(issuable_type, issuable_sidebar) } = loading_icon(css_class: 'gl-vertical-align-text-bottom hidden block-loading')
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) } - if can_edit_issuable
= multi_label_name(selected_labels, "Labels") = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { qa_selector: "edit_labels_link", track_label: "right_sidebar", track_property: "labels", track_event: "click_edit_button", track_value: "" }
= icon('chevron-down', 'aria-hidden': 'true') .value.issuable-show-labels.dont-hide.hide-collapsed{ class: ("has-labels" if selected_labels.any?), data: { qa_selector: 'labels_block' } }
.dropdown-menu.dropdown-select.dropdown-menu-paging.qa-dropdown-menu-labels.dropdown-menu-labels.dropdown-menu-selectable.dropdown-extended-height - if selected_labels.any?
= render partial: "shared/issuable/label_page_default" - selected_labels.each do |label_hash|
- if issuable_sidebar.dig(:current_user, :can_admin_label) = render_label(label_from_hash(label_hash).present(issuable_subject: nil), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title]), dataset: { qa_selector: 'label', qa_label_name: label_hash[:title] })
= render partial: "shared/issuable/label_page_create" - else
%span.no-value
= _('None')
.selectbox.hide-collapsed
- selected_labels.each do |label|
= hidden_field_tag "#{issuable_type}[label_names][]", label[:id], id: nil
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: sidebar_label_dropdown_data(issuable_type, issuable_sidebar) }
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
= icon('chevron-down', 'aria-hidden': 'true')
.dropdown-menu.dropdown-select.dropdown-menu-paging.qa-dropdown-menu-labels.dropdown-menu-labels.dropdown-menu-selectable.dropdown-extended-height
= render partial: "shared/issuable/label_page_default"
- if issuable_sidebar.dig(:current_user, :can_admin_label)
= render partial: "shared/issuable/label_page_create"
= render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar = render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar
......
...@@ -2951,6 +2951,9 @@ msgstr "" ...@@ -2951,6 +2951,9 @@ msgstr ""
msgid "An error occurred while updating approvers" msgid "An error occurred while updating approvers"
msgstr "" msgstr ""
msgid "An error occurred while updating labels."
msgstr ""
msgid "An error occurred while updating the comment" msgid "An error occurred while updating the comment"
msgstr "" msgstr ""
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Group label on issue' do RSpec.describe 'Group label on issue' do
it 'renders link to the project issues page' do it 'renders link to the project issues page', :js do
group = create(:group) group = create(:group)
project = create(:project, :public, namespace: group) project = create(:project, :public, namespace: group)
feature = create(:group_label, group: group, title: 'feature') feature = create(:group_label, group: group, title: 'feature')
...@@ -14,6 +14,6 @@ RSpec.describe 'Group label on issue' do ...@@ -14,6 +14,6 @@ RSpec.describe 'Group label on issue' do
link = find('.issuable-show-labels a') link = find('.issuable-show-labels a')
expect(link[:href]).to eq(label_link) expect(CGI.unescape(link[:href])).to include(CGI.unescape(label_link))
end end
end end
...@@ -168,7 +168,7 @@ RSpec.describe 'Issue Sidebar' do ...@@ -168,7 +168,7 @@ RSpec.describe 'Issue Sidebar' do
it 'escapes XSS when viewing issue labels' do it 'escapes XSS when viewing issue labels' do
page.within('.block.labels') do page.within('.block.labels') do
find('.edit-link').click click_on 'Edit'
expect(page).to have_content '<script>alert("xss");</script>' expect(page).to have_content '<script>alert("xss");</script>'
end end
...@@ -179,7 +179,7 @@ RSpec.describe 'Issue Sidebar' do ...@@ -179,7 +179,7 @@ RSpec.describe 'Issue Sidebar' do
before do before do
issue.update(labels: [label]) issue.update(labels: [label])
page.within('.block.labels') do page.within('.block.labels') do
find('.edit-link').click click_on 'Edit'
end end
end end
...@@ -286,7 +286,7 @@ RSpec.describe 'Issue Sidebar' do ...@@ -286,7 +286,7 @@ RSpec.describe 'Issue Sidebar' do
end end
it 'does not have a option to edit labels' do it 'does not have a option to edit labels' do
expect(page).not_to have_selector('.block.labels .edit-link') expect(page).not_to have_selector('.block.labels .js-sidebar-dropdown-toggle')
end end
context 'interacting with collapsed sidebar', :js do context 'interacting with collapsed sidebar', :js do
......
...@@ -35,12 +35,12 @@ RSpec.describe 'List issue resource label events', :js do ...@@ -35,12 +35,12 @@ RSpec.describe 'List issue resource label events', :js do
context 'when user adds label to the issue' do context 'when user adds label to the issue' do
def toggle_labels(labels) def toggle_labels(labels)
page.within '.labels' do page.within '.labels' do
click_link 'Edit' click_on 'Edit'
wait_for_requests wait_for_requests
labels.each { |label| click_link label } labels.each { |label| click_link label }
click_link 'Edit' click_on 'Edit'
wait_for_requests wait_for_requests
end end
end end
......
...@@ -95,11 +95,12 @@ RSpec.describe "Issues > User edits issue", :js do ...@@ -95,11 +95,12 @@ RSpec.describe "Issues > User edits issue", :js do
describe 'update labels' do describe 'update labels' do
it 'will not send ajax request when no data is changed' do it 'will not send ajax request when no data is changed' do
page.within '.labels' do page.within '.labels' do
click_link 'Edit' click_on 'Edit'
find('.dropdown-menu-close', match: :first).click find('.dropdown-title button').click
expect(page).not_to have_selector('.block-loading') expect(page).not_to have_selector('.block-loading')
expect(page).not_to have_selector('.gl-spinner')
end end
end end
end end
......
...@@ -42,12 +42,12 @@ RSpec.describe 'Labels Hierarchy', :js do ...@@ -42,12 +42,12 @@ RSpec.describe 'Labels Hierarchy', :js do
it 'does not find child group labels on dropdown' do it 'does not find child group labels on dropdown' do
page.within('.block.labels') do page.within('.block.labels') do
find('.edit-link').click click_on 'Edit'
end
wait_for_requests wait_for_requests
expect(page).not_to have_selector('.badge', text: child_group_label.title) expect(page).not_to have_text(child_group_label.title)
end
end end
end end
......
import { createLocalVue, shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import Vuex from 'vuex';
import {
mockLabels,
mockRegularLabel,
} from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data';
import axios from '~/lib/utils/axios_utils';
import SidebarLabels from '~/sidebar/components/labels/sidebar_labels.vue';
import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('sidebar labels', () => {
let axiosMock;
let wrapper;
const store = new Vuex.Store(labelsSelectModule());
const defaultProps = {
allowLabelCreate: true,
allowLabelEdit: true,
allowScopedLabels: true,
canEdit: true,
iid: '1',
initiallySelectedLabels: mockLabels,
issuableType: 'issue',
labelsFetchPath: '/gitlab-org/gitlab-test/-/labels.json?include_ancestor_groups=true',
labelsManagePath: '/gitlab-org/gitlab-test/-/labels',
labelsUpdatePath: '/gitlab-org/gitlab-test/-/issues/1.json',
projectIssuesPath: '/gitlab-org/gitlab-test/-/issues',
projectPath: 'gitlab-org/gitlab-test',
};
const findLabelsSelect = () => wrapper.find(LabelsSelect);
const mountComponent = () => {
wrapper = shallowMount(SidebarLabels, {
localVue,
provide: {
...defaultProps,
},
store,
});
};
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
axiosMock.restore();
});
describe('LabelsSelect props', () => {
beforeEach(() => {
mountComponent();
});
it('are as expected', () => {
expect(findLabelsSelect().props()).toMatchObject({
allowLabelCreate: defaultProps.allowLabelCreate,
allowLabelEdit: defaultProps.allowLabelEdit,
allowMultiselect: true,
allowScopedLabels: defaultProps.allowScopedLabels,
footerCreateLabelTitle: 'Create project label',
footerManageLabelTitle: 'Manage project labels',
labelsCreateTitle: 'Create project label',
labelsFetchPath: defaultProps.labelsFetchPath,
labelsFilterBasePath: defaultProps.projectIssuesPath,
labelsManagePath: defaultProps.labelsManagePath,
labelsSelectInProgress: false,
selectedLabels: defaultProps.initiallySelectedLabels,
variant: DropdownVariant.Sidebar,
});
});
});
describe('when labels are changed', () => {
beforeEach(() => {
mountComponent();
});
it('makes an API call to update labels', async () => {
const labels = [
{
...mockRegularLabel,
set: false,
},
{
id: 40,
title: 'Security',
color: '#ddd',
text_color: '#fff',
set: true,
},
{
id: 55,
title: 'Tooling',
color: '#ddd',
text_color: '#fff',
set: false,
},
];
findLabelsSelect().vm.$emit('updateSelectedLabels', labels);
await axios.waitForAll();
const expected = {
[defaultProps.issuableType]: {
label_ids: [27, 28, 40],
},
};
expect(axiosMock.history.put[0].data).toEqual(JSON.stringify(expected));
});
});
});
...@@ -150,11 +150,10 @@ describe('LabelsSelectRoot', () => { ...@@ -150,11 +150,10 @@ describe('LabelsSelectRoot', () => {
expect(wrapper.find(DropdownTitle).exists()).toBe(true); expect(wrapper.find(DropdownTitle).exists()).toBe(true);
}); });
it('renders `dropdown-value` component with slot when `showDropdownButton` prop is `false`', () => { it('renders `dropdown-value` component', () => {
const wrapperDropdownValue = createComponent(mockConfig, { const wrapperDropdownValue = createComponent(mockConfig, {
default: 'None', default: 'None',
}); });
wrapperDropdownValue.vm.$store.state.showDropdownButton = false;
return wrapperDropdownValue.vm.$nextTick(() => { return wrapperDropdownValue.vm.$nextTick(() => {
const valueComp = wrapperDropdownValue.find(DropdownValue); const valueComp = wrapperDropdownValue.find(DropdownValue);
......
...@@ -259,6 +259,21 @@ describe('LabelsSelect Actions', () => { ...@@ -259,6 +259,21 @@ describe('LabelsSelect Actions', () => {
}); });
}); });
describe('replaceSelectedLabels', () => {
it('replaces `state.selectedLabels`', done => {
const selectedLabels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
testAction(
actions.replaceSelectedLabels,
selectedLabels,
state,
[{ type: types.REPLACE_SELECTED_LABELS, payload: selectedLabels }],
[],
done,
);
});
});
describe('updateSelectedLabels', () => { describe('updateSelectedLabels', () => {
it('updates `state.labels` based on provided `labels` param', done => { it('updates `state.labels` based on provided `labels` param', done => {
const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]; const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
......
...@@ -152,6 +152,19 @@ describe('LabelsSelect Mutations', () => { ...@@ -152,6 +152,19 @@ describe('LabelsSelect Mutations', () => {
}); });
}); });
describe(`${types.REPLACE_SELECTED_LABELS}`, () => {
it('replaces `state.selectedLabels`', () => {
const state = {
selectedLabels: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }],
};
const newSelectedLabels = [{ id: 2 }, { id: 5 }];
mutations[types.REPLACE_SELECTED_LABELS](state, newSelectedLabels);
expect(state.selectedLabels).toEqual(newSelectedLabels);
});
});
describe(`${types.UPDATE_SELECTED_LABELS}`, () => { describe(`${types.UPDATE_SELECTED_LABELS}`, () => {
const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]; const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
......
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