Commit 1cc19f7a authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Updated specs for filter bar

Updates the filter bar to add
the additional search tokens and updates
related tests
parent f1f5371a
<script>
import { mapState } from 'vuex';
import { mapState, mapActions } from 'vuex';
import { GlFilteredSearch } from '@gitlab/ui';
import { __ } from '~/locale';
import MilestoneToken from '../../shared/components/tokens/milestone_token.vue';
import LabelToken from '../../shared/components/tokens/label_token.vue';
import UserToken from '../../shared/components/tokens/user_token.vue';
export default {
name: 'FilterBar',
......@@ -16,20 +20,114 @@ export default {
},
data() {
return {
searchTerms: [],
value: [],
};
},
computed: {
...mapState('filters', ['milestonesPath', 'labelsPath']),
...mapState('filters', {
milestones: state => state.milestones.data,
milestonesLoading: state => state.milestones.isLoading,
labels: state => state.labels.data,
labelsLoading: state => state.labels.isLoading,
authors: state => state.authors.data,
authorsLoading: state => state.authors.isLoading,
assignees: state => state.assignees.data,
assigneesLoading: state => state.assignees.isLoading,
}),
availableTokens() {
return [
{
icon: 'clock',
title: __('Milestone'),
type: 'milestone',
token: MilestoneToken,
milestones: this.milestones,
unique: true,
symbol: '%',
isLoading: this.milestonesLoading,
operators: [{ value: '=', description: 'is', default: 'true' }],
},
{
icon: 'labels',
title: __('Label'),
type: 'labels',
token: LabelToken,
labels: this.labels,
unique: false,
symbol: '~',
isLoading: this.labelsLoading,
operators: [{ value: '=', description: 'is', default: 'true' }],
},
{
icon: 'pencil',
title: __('Author'),
type: 'author',
token: UserToken,
users: this.authors,
unique: true,
isLoading: this.authorsLoading,
operators: [{ value: '=', description: 'is', default: 'true' }],
},
{
icon: 'user',
title: __('Assignees'),
type: 'assignees',
token: UserToken,
users: this.assignees,
unique: false,
isLoading: this.assigneesLoading,
operators: [{ value: '=', description: 'is', default: 'true' }],
},
];
},
},
methods: {
...mapActions('filters', ['setFilters']),
processFilters(filters) {
return filters.reduce((acc, token) => {
const { type, value } = token;
const { operator } = value;
let tokenValue = value.data;
// remove wrapping double quotes which were added for token values that include spaces
if (
(tokenValue[0] === "'" && tokenValue[tokenValue.length - 1] === "'") ||
(tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')
) {
tokenValue = tokenValue.slice(1, -1);
}
if (!acc[type]) {
acc[type] = [];
}
acc[type].push({ value: tokenValue, operator });
return acc;
}, {});
},
filteredSearchSubmit(filters) {
const { labels, milestone, author, assignees } = this.processFilters(filters);
this.setFilters({
selectedAuthor: author ? author[0].value : null,
selectedMilestone: milestone ? milestone[0].value : null,
selectedAssignees: assignees ? assignees.map(a => a.value) : [],
selectedLabels: labels ? labels.map(l => l.value) : [],
});
},
},
};
</script>
<template>
<gl-filtered-search
v-model="value"
:disabled="disabled"
:v-model="searchTerms"
:placeholder="__('Filter results')"
:clear-button-title="__('Clear')"
:close-button-title="__('Close')"
:available-tokens="availableTokens"
@submit="filteredSearchSubmit"
/>
</template>
......@@ -234,6 +234,9 @@ export const removeStage = ({ dispatch, state }, stageId) => {
.catch(error => dispatch('receiveRemoveStageError', error));
};
export const setSelectedFilters = ({ commit }, filters) =>
commit(types.SET_SELECTED_FILTERS, filters);
export const initializeCycleAnalyticsSuccess = ({ commit }) =>
commit(types.INITIALIZE_CYCLE_ANALYTICS_SUCCESS);
......
import * as types from './mutation_types';
// eslint-disable-next-line import/prefer-default-export
const appendExtension = path => (path.indexOf('.') > -1 ? path : `${path}.json`);
export const setPaths = ({ commit }, { milestonesPath = '', labelsPath = '' }) => {
commit(types.SET_MILESTONES_PATH, milestonesPath);
commit(types.SET_LABELS_PATH, labelsPath);
commit(types.SET_MILESTONES_PATH, appendExtension(milestonesPath));
commit(types.SET_LABELS_PATH, appendExtension(labelsPath));
};
export const setFilters = ({ dispatch, state }, params) => {
const { selectedLabels: labelNames = [], ...rest } = params;
const {
labels: { data: labelsList = [] },
} = state;
const selectedLabels = labelsList.filter(({ title }) => labelNames.includes(title));
const nextFilters = {
...rest,
selectedLabels,
};
return dispatch('setSelectedFilters', nextFilters, { root: true });
};
export default () => ({
milestonesPath: '',
labelsPath: '',
milestones: {
isLoading: false,
data: [],
},
labels: {
isLoading: false,
data: [],
},
authors: {
isLoading: false,
data: [],
},
assignees: {
isLoading: false,
data: [],
},
});
......@@ -4,6 +4,7 @@ export const SET_SELECTED_GROUP = 'SET_SELECTED_GROUP';
export const SET_SELECTED_PROJECTS = 'SET_SELECTED_PROJECTS';
export const SET_SELECTED_STAGE = 'SET_SELECTED_STAGE';
export const SET_DATE_RANGE = 'SET_DATE_RANGE';
export const SET_SELECTED_FILTERS = 'SET_SELECTED_FILTERS';
export const REQUEST_CYCLE_ANALYTICS_DATA = 'REQUEST_CYCLE_ANALYTICS_DATA';
export const RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS = 'RECEIVE_CYCLE_ANALYTICS_DATA_SUCCESS';
......
......@@ -115,4 +115,5 @@ export default {
state.isSavingStageOrder = false;
state.errorSavingStageOrder = true;
},
[types.SET_SELECTED_FILTERS]() {},
};
......@@ -37,9 +37,6 @@ export default {
value: this.getEscapedText(label.title),
}));
},
hideDefault() {
return this.config?.hideDefaultOptions;
},
},
methods: {
getEscapedText(text) {
......@@ -80,15 +77,13 @@ export default {
<template #suggestions>
<gl-loading-icon v-if="config.isLoading" />
<template v-else>
<div v-if="!hideDefault">
<gl-filtered-search-suggestion
v-for="suggestion in $options.defaultSuggestions"
:key="suggestion.value"
:value="suggestion.value"
>{{ suggestion.text }}</gl-filtered-search-suggestion
>
<gl-dropdown-divider v-if="config.isLoading || filteredLabels.length" />
</div>
<gl-filtered-search-suggestion
v-for="suggestion in $options.defaultSuggestions"
:key="suggestion.value"
:value="suggestion.value"
>{{ suggestion.text }}</gl-filtered-search-suggestion
>
<gl-dropdown-divider v-if="config.isLoading || filteredLabels.length" />
<gl-filtered-search-suggestion
v-for="label in filteredLabels"
ref="labelItem"
......
......@@ -39,7 +39,7 @@ export default {
},
},
methods: {
getEscapedText(text = null) {
getEscapedText(text) {
let escapedText = text;
const hasSpace = text.indexOf(' ') !== -1;
const hasDoubleQuote = text.indexOf('"') !== -1;
......
......@@ -4,8 +4,7 @@ import testAction from 'helpers/vuex_action_helper';
import * as actions from 'ee/analytics/cycle_analytics/store/modules/filters/actions';
import * as types from 'ee/analytics/cycle_analytics/store/modules/filters/mutation_types';
import initialState from 'ee/analytics/cycle_analytics/store/modules/filters/state';
import createFlash from '~/flash';
import { filterMilestones, filterUsers, filterLabels } from '../../../mock_data';
import { filterLabels } from '../../../mock_data';
const milestonesPath = 'fake_milestones_path';
const labelsPath = 'fake_labels_path';
......
import mutations from 'ee/analytics/cycle_analytics/store/modules/filters/mutations';
import * as types from 'ee/analytics/cycle_analytics/store/modules/filters/mutation_types';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { filterMilestones, filterUsers, filterLabels } from '../../../mock_data';
let state = null;
const milestones = filterMilestones.map(convertObjectPropsToCamelCase);
const users = filterUsers.map(convertObjectPropsToCamelCase);
const labels = filterLabels.map(convertObjectPropsToCamelCase);
describe('Filters mutations', () => {
beforeEach(() => {
state = { initialTokens: {}, milestones: {}, authors: {}, labels: {}, assignees: {} };
......
......@@ -56,23 +56,6 @@ describe('LabelToken', () => {
});
});
describe('hideDefaultOptions = true', () => {
it('does not render defualt suggestions', () => {
createComponent(
{
config: {
...defaultConfig,
hideDefaultOptions: true,
},
},
{ stubs },
);
const html = wrapper.html();
expect(html).not.toContain('None');
expect(html).not.toContain('Any');
});
});
describe('when no search term is given', () => {
it('renders two label suggestions', () => {
createComponent(null, { stubs });
......
......@@ -35,37 +35,37 @@ export const mockLabels = [
];
export const mockUsers = [
{
id: 31,
name: 'VSM User2',
username: 'vsm-user-2-1589776313',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/762398957a8c6e04eed16da88098899d?s=80\u0026d=identicon',
web_url: 'http://127.0.0.1:3001/vsm-user-2-1589776313',
access_level: 30,
expires_at: null,
},
{
id: 32,
name: 'VSM User3',
username: 'vsm-user-3-1589776313',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/f78932237e8a5c5376b65a709824802f?s=80\u0026d=identicon',
web_url: 'http://127.0.0.1:3001/vsm-user-3-1589776313',
access_level: 30,
expires_at: null,
},
{
id: 33,
name: 'VSM User4',
username: 'vsm-user-4-1589776313',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/ab506dc600d1a941e4d77d5ceeeba73f?s=80\u0026d=identicon',
web_url: 'http://127.0.0.1:3001/vsm-user-4-1589776313',
access_level: 30,
expires_at: null,
},
];
{
id: 31,
name: 'VSM User2',
username: 'vsm-user-2-1589776313',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/762398957a8c6e04eed16da88098899d?s=80\u0026d=identicon',
web_url: 'http://127.0.0.1:3001/vsm-user-2-1589776313',
access_level: 30,
expires_at: null,
},
{
id: 32,
name: 'VSM User3',
username: 'vsm-user-3-1589776313',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/f78932237e8a5c5376b65a709824802f?s=80\u0026d=identicon',
web_url: 'http://127.0.0.1:3001/vsm-user-3-1589776313',
access_level: 30,
expires_at: null,
},
{
id: 33,
name: 'VSM User4',
username: 'vsm-user-4-1589776313',
state: 'active',
avatar_url:
'https://www.gravatar.com/avatar/ab506dc600d1a941e4d77d5ceeeba73f?s=80\u0026d=identicon',
web_url: 'http://127.0.0.1:3001/vsm-user-4-1589776313',
access_level: 30,
expires_at: null,
},
];
......@@ -3002,6 +3002,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
msgid "Assignees"
msgstr ""
msgid "Assigns %{assignee_users_sentence}."
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