Commit 43c249bb authored by Scott Hampton's avatar Scott Hampton

Merge branch 'fix-broken-vsa-label-filter' into 'master'

Fix mismatched custom vue event names

See merge request gitlab-org/gitlab!55958
parents 3a0a841c 94ccb0a8
...@@ -159,7 +159,7 @@ export default { ...@@ -159,7 +159,7 @@ export default {
'cursor-not-allowed': disabled, 'cursor-not-allowed': disabled,
}" }"
:active="isSelectedLabel(label.id)" :active="isSelectedLabel(label.id)"
@click.prevent="$emit('select-label', label.id, selectedLabelIds)" @click.prevent="$emit('select-label', label.id)"
> >
<gl-icon <gl-icon
v-if="multiselect && isSelectedLabel(label.id)" v-if="multiselect && isSelectedLabel(label.id)"
......
<script> <script>
import { GlDropdownDivider, GlSegmentedControl, GlIcon } from '@gitlab/ui'; import { GlDropdownDivider, GlSegmentedControl, GlIcon, GlSprintf } from '@gitlab/ui';
import { deprecatedCreateFlash as createFlash } from '~/flash'; import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import { import {
...@@ -18,6 +18,7 @@ export default { ...@@ -18,6 +18,7 @@ export default {
GlDropdownDivider, GlDropdownDivider,
GlIcon, GlIcon,
LabelsSelector, LabelsSelector,
GlSprintf,
}, },
props: { props: {
selectedLabelIds: { selectedLabelIds: {
...@@ -45,25 +46,14 @@ export default { ...@@ -45,25 +46,14 @@ export default {
value, value,
})); }));
}, },
selectedFiltersText() { selectedSubjectFilter() {
const { subjectFilter, selectedLabelIds } = this; const { subjectFilter } = this;
const subjectFilterText = TASKS_BY_TYPE_SUBJECT_FILTER_OPTIONS[subjectFilter] return TASKS_BY_TYPE_SUBJECT_FILTER_OPTIONS[subjectFilter]
? TASKS_BY_TYPE_SUBJECT_FILTER_OPTIONS[subjectFilter] ? TASKS_BY_TYPE_SUBJECT_FILTER_OPTIONS[subjectFilter]
: TASKS_BY_TYPE_SUBJECT_FILTER_OPTIONS[TASKS_BY_TYPE_SUBJECT_ISSUE]; : TASKS_BY_TYPE_SUBJECT_FILTER_OPTIONS[TASKS_BY_TYPE_SUBJECT_ISSUE];
return sprintf(
s__('CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} labels'),
{
subjectFilterText,
selectedLabelsCount: selectedLabelIds.length,
},
);
}, },
selectedLabelLimitText() { selectedLabelsCount() {
const { selectedLabelIds, maxLabels } = this; return this.selectedLabelIds.length;
return sprintf(s__('CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)'), {
selectedLabelsCount: selectedLabelIds.length,
maxLabels,
});
}, },
maxLabelsSelected() { maxLabelsSelected() {
return this.selectedLabelIds.length >= this.maxLabels; return this.selectedLabelIds.length >= this.maxLabels;
...@@ -77,7 +67,7 @@ export default { ...@@ -77,7 +67,7 @@ export default {
handleLabelSelected(value) { handleLabelSelected(value) {
removeFlash('notice'); removeFlash('notice');
if (this.canUpdateLabelFilters(value)) { if (this.canUpdateLabelFilters(value)) {
this.$emit('updateFilter', { filter: TASKS_BY_TYPE_FILTERS.LABEL, value }); this.$emit('update-filter', { filter: TASKS_BY_TYPE_FILTERS.LABEL, value });
} else { } else {
const { maxLabels } = this; const { maxLabels } = this;
const message = sprintf( const message = sprintf(
...@@ -97,10 +87,24 @@ export default { ...@@ -97,10 +87,24 @@ export default {
> >
<div class="flex-column"> <div class="flex-column">
<h4>{{ s__('CycleAnalytics|Tasks by type') }}</h4> <h4>{{ s__('CycleAnalytics|Tasks by type') }}</h4>
<p v-if="hasData">{{ selectedFiltersText }}</p> <p v-if="hasData">
<gl-sprintf
:message="
n__(
'CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} label',
'CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} labels',
selectedLabelsCount,
)
"
>
<template #selectedLabelsCount>{{ selectedLabelsCount }}</template>
<template #subjectFilterText>{{ selectedSubjectFilter }}</template>
</gl-sprintf>
</p>
</div> </div>
<div class="flex-column"> <div class="flex-column">
<labels-selector <labels-selector
data-testid="type-of-work-filters-label"
:default-selected-labels-ids="selectedLabelIds" :default-selected-labels-ids="selectedLabelIds"
:max-labels="maxLabels" :max-labels="maxLabels"
:aria-label="__('CycleAnalytics|Display chart filters')" :aria-label="__('CycleAnalytics|Display chart filters')"
...@@ -108,7 +112,7 @@ export default { ...@@ -108,7 +112,7 @@ export default {
aria-expanded="false" aria-expanded="false"
multiselect multiselect
right right
@selectLabel="handleLabelSelected" @select-label="handleLabelSelected"
> >
<template #label-dropdown-button> <template #label-dropdown-button>
<gl-icon class="vertical-align-top" name="settings" /> <gl-icon class="vertical-align-top" name="settings" />
...@@ -118,11 +122,12 @@ export default { ...@@ -118,11 +122,12 @@ export default {
<div class="mb-3 px-3"> <div class="mb-3 px-3">
<p class="font-weight-bold text-left mb-2">{{ s__('CycleAnalytics|Show') }}</p> <p class="font-weight-bold text-left mb-2">{{ s__('CycleAnalytics|Show') }}</p>
<gl-segmented-control <gl-segmented-control
data-testid="type-of-work-filters-subject"
:checked="subjectFilter" :checked="subjectFilter"
:options="subjectFilterOptions" :options="subjectFilterOptions"
@input=" @input="
(value) => (value) =>
$emit('updateFilter', { filter: $options.TASKS_BY_TYPE_FILTERS.SUBJECT, value }) $emit('update-filter', { filter: $options.TASKS_BY_TYPE_FILTERS.SUBJECT, value })
" "
/> />
</div> </div>
...@@ -130,7 +135,16 @@ export default { ...@@ -130,7 +135,16 @@ export default {
<div class="mb-3 px-3"> <div class="mb-3 px-3">
<p class="font-weight-bold text-left my-2"> <p class="font-weight-bold text-left my-2">
{{ s__('CycleAnalytics|Select labels') }} {{ s__('CycleAnalytics|Select labels') }}
<br /><small>{{ selectedLabelLimitText }}</small> <br /><small>
<gl-sprintf
:message="
s__('CycleAnalytics|%{selectedLabelsCount} selected (%{maxLabels} max)')
"
>
<template #selectedLabelsCount>{{ selectedLabelsCount }}</template>
<template #maxLabels>{{ maxLabels }}</template>
</gl-sprintf>
</small>
</p> </p>
</div> </div>
</template> </template>
......
...@@ -81,7 +81,7 @@ export default { ...@@ -81,7 +81,7 @@ export default {
:has-data="hasData" :has-data="hasData"
:selected-label-ids="selectedLabelIdsFilter" :selected-label-ids="selectedLabelIdsFilter"
:subject-filter="selectedSubjectFilter" :subject-filter="selectedSubjectFilter"
@updateFilter="onUpdateFilter" @update-filter="onUpdateFilter"
/> />
<tasks-by-type-chart <tasks-by-type-chart
v-if="hasData" v-if="hasData"
......
---
title: Ensure task by type filters refresh the chart data
merge_request: 55958
author:
type: fixed
...@@ -82,6 +82,8 @@ RSpec.describe 'Value stream analytics charts', :js do ...@@ -82,6 +82,8 @@ RSpec.describe 'Value stream analytics charts', :js do
end end
describe 'Tasks by type chart', :js do describe 'Tasks by type chart', :js do
filters_selector = '.js-tasks-by-type-chart-filters'
before do before do
stub_licensed_features(cycle_analytics_for_groups: true, type_of_work_analytics: true) stub_licensed_features(cycle_analytics_for_groups: true, type_of_work_analytics: true)
...@@ -94,6 +96,9 @@ RSpec.describe 'Value stream analytics charts', :js do ...@@ -94,6 +96,9 @@ RSpec.describe 'Value stream analytics charts', :js do
context 'enabled' do context 'enabled' do
context 'with data available' do context 'with data available' do
before do before do
mr_issue = create(:labeled_issue, created_at: 5.days.ago, project: create(:project, group: group), labels: [group_label2])
create(:merge_request, iid: mr_issue.id, created_at: 3.days.ago, source_project: project, labels: [group_label1, group_label2])
3.times do |i| 3.times do |i|
create(:labeled_issue, created_at: i.days.ago, project: create(:project, group: group), labels: [group_label1]) create(:labeled_issue, created_at: i.days.ago, project: create(:project, group: group), labels: [group_label1])
create(:labeled_issue, created_at: i.days.ago, project: create(:project, group: group), labels: [group_label2]) create(:labeled_issue, created_at: i.days.ago, project: create(:project, group: group), labels: [group_label2])
...@@ -113,7 +118,24 @@ RSpec.describe 'Value stream analytics charts', :js do ...@@ -113,7 +118,24 @@ RSpec.describe 'Value stream analytics charts', :js do
end end
it 'has chart filters' do it 'has chart filters' do
expect(page).to have_css('.js-tasks-by-type-chart-filters') expect(page).to have_css(filters_selector)
end
it 'can update the filters' do
page.within filters_selector do
find('.dropdown-toggle').click
first_selected_label = all('[data-testid="type-of-work-filters-label"] .dropdown-item.active').first
first_selected_label.click
end
expect(page).to have_text('Showing Issues and 1 label')
page.within filters_selector do
find('.dropdown-toggle').click
find('[data-testid="type-of-work-filters-subject"] label', text: 'Merge Requests').click
end
expect(page).to have_text('Showing Merge Requests and 1 label')
end end
end end
......
...@@ -100,7 +100,7 @@ describe('Value Stream Analytics LabelsSelector', () => { ...@@ -100,7 +100,7 @@ describe('Value Stream Analytics LabelsSelector', () => {
return waitForPromises(); return waitForPromises();
}); });
it('will emit the "selectLabel" event', () => { it('will emit the "select-label" event', () => {
expect(wrapper.emitted('select-label')).toBeUndefined(); expect(wrapper.emitted('select-label')).toBeUndefined();
const elem = wrapper.findAll('.dropdown-item').at(1); const elem = wrapper.findAll('.dropdown-item').at(1);
......
import { GlDropdownItem, GlSegmentedControl } from '@gitlab/ui'; import { GlDropdownItem, GlSegmentedControl, GlSprintf } from '@gitlab/ui';
import { shallowMount, mount, createLocalVue } from '@vue/test-utils'; import { shallowMount, mount, createLocalVue } from '@vue/test-utils';
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
...@@ -56,6 +56,7 @@ function createComponent({ props = {}, mountFn = shallowMount } = {}) { ...@@ -56,6 +56,7 @@ function createComponent({ props = {}, mountFn = shallowMount } = {}) {
}, },
stubs: { stubs: {
LabelsSelector, LabelsSelector,
GlSprintf,
}, },
}); });
} }
...@@ -63,7 +64,10 @@ function createComponent({ props = {}, mountFn = shallowMount } = {}) { ...@@ -63,7 +64,10 @@ function createComponent({ props = {}, mountFn = shallowMount } = {}) {
describe('TasksByTypeFilters', () => { describe('TasksByTypeFilters', () => {
let wrapper = null; let wrapper = null;
let mock = null; let mock = null;
const selectedFilterText = (count = 1) => `Showing Issues and ${count} labels`; const selectedFilterText = (count = 1) => {
const affix = count > 1 ? 'labels' : 'label';
return `Showing Issues and ${count} ${affix}`;
};
beforeEach(() => { beforeEach(() => {
mock = mockGroupLabelsRequest(); mock = mockGroupLabelsRequest();
...@@ -110,13 +114,13 @@ describe('TasksByTypeFilters', () => { ...@@ -110,13 +114,13 @@ describe('TasksByTypeFilters', () => {
return waitForPromises(); return waitForPromises();
}); });
it('emits the `updateFilter` event when a label is selected', () => { it('emits the `update-filter` event when a label is selected', () => {
expect(wrapper.emitted('updateFilter')).toBeUndefined(); expect(wrapper.emitted('update-filter')).toBeUndefined();
wrapper.find(LabelsSelector).vm.$emit('selectLabel', groupLabels[0].id); wrapper.find(LabelsSelector).vm.$emit('select-label', groupLabels[0].id);
expect(wrapper.emitted('updateFilter')).toBeDefined(); expect(wrapper.emitted('update-filter')).toBeDefined();
expect(wrapper.emitted('updateFilter')[0]).toEqual([ expect(wrapper.emitted('update-filter')[0]).toEqual([
{ filter: TASKS_BY_TYPE_FILTERS.LABEL, value: groupLabels[0].id }, { filter: TASKS_BY_TYPE_FILTERS.LABEL, value: groupLabels[0].id },
]); ]);
}); });
...@@ -157,7 +161,7 @@ describe('TasksByTypeFilters', () => { ...@@ -157,7 +161,7 @@ describe('TasksByTypeFilters', () => {
}); });
return waitForPromises().then(() => { return waitForPromises().then(() => {
wrapper.find(LabelsSelector).vm.$emit('selectLabel', groupLabels[2].id); wrapper.find(LabelsSelector).vm.$emit('select-label', groupLabels[2].id);
}); });
}); });
...@@ -166,7 +170,7 @@ describe('TasksByTypeFilters', () => { ...@@ -166,7 +170,7 @@ describe('TasksByTypeFilters', () => {
}); });
it('should not allow selecting another label', () => { it('should not allow selecting another label', () => {
expect(wrapper.emitted('updateFilter')).toBeUndefined(); expect(wrapper.emitted('update-filter')).toBeUndefined();
}); });
it('should display a message', () => { it('should display a message', () => {
...@@ -183,15 +187,15 @@ describe('TasksByTypeFilters', () => { ...@@ -183,15 +187,15 @@ describe('TasksByTypeFilters', () => {
expect(findSelectedSubjectFilters(wrapper)).toBe(TASKS_BY_TYPE_SUBJECT_ISSUE); expect(findSelectedSubjectFilters(wrapper)).toBe(TASKS_BY_TYPE_SUBJECT_ISSUE);
}); });
it('emits the `updateFilter` event when a subject filter is clicked', () => { it('emits the `update-filter` event when a subject filter is clicked', () => {
wrapper = createComponent({ mountFn: mount }); wrapper = createComponent({ mountFn: mount });
expect(wrapper.emitted('updateFilter')).toBeUndefined(); expect(wrapper.emitted('update-filter')).toBeUndefined();
findSubjectFilters(wrapper).findAll('label:not(.active)').at(0).trigger('click'); findSubjectFilters(wrapper).findAll('label:not(.active)').at(0).trigger('click');
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(wrapper.emitted('updateFilter')).toBeDefined(); expect(wrapper.emitted('update-filter')).toBeDefined();
expect(wrapper.emitted('updateFilter')[0]).toEqual([ expect(wrapper.emitted('update-filter')[0]).toEqual([
{ {
filter: TASKS_BY_TYPE_FILTERS.SUBJECT, filter: TASKS_BY_TYPE_FILTERS.SUBJECT,
value: TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST, value: TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST,
......
...@@ -105,7 +105,7 @@ describe('TypeOfWorkCharts', () => { ...@@ -105,7 +105,7 @@ describe('TypeOfWorkCharts', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
findSubjectFilters(wrapper).vm.$emit('updateFilter', payload); findSubjectFilters(wrapper).vm.$emit('update-filter', payload);
return wrapper.vm.$nextTick(); return wrapper.vm.$nextTick();
}); });
......
...@@ -9326,8 +9326,10 @@ msgstr "" ...@@ -9326,8 +9326,10 @@ msgstr ""
msgid "CycleAnalytics|Show" msgid "CycleAnalytics|Show"
msgstr "" msgstr ""
msgid "CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} labels" msgid "CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} label"
msgstr "" msgid_plural "CycleAnalytics|Showing %{subjectFilterText} and %{selectedLabelsCount} labels"
msgstr[0] ""
msgstr[1] ""
msgid "CycleAnalytics|Showing data for group '%{groupName}' and %{selectedProjectCount} projects from %{startDate} to %{endDate}" msgid "CycleAnalytics|Showing data for group '%{groupName}' and %{selectedProjectCount} projects from %{startDate} to %{endDate}"
msgstr "" msgstr ""
......
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