diff --git a/app/assets/javascripts/issues_list/components/issues_list_app.vue b/app/assets/javascripts/issues_list/components/issues_list_app.vue index 4b7b12f6b99b861fbbe53314c038b7d98b04a001..2c36f66591d4012b0eef5ea3c2520468a913415a 100644 --- a/app/assets/javascripts/issues_list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue @@ -1,5 +1,13 @@ diff --git a/app/assets/javascripts/issues_list/constants.js b/app/assets/javascripts/issues_list/constants.js index 4a4608592013a6b5b6ea6c682b4d9638a9f5503d..60a211ec8c042c3c14316dfb62fde510b914e24e 100644 --- a/app/assets/javascripts/issues_list/constants.js +++ b/app/assets/javascripts/issues_list/constants.js @@ -298,6 +298,16 @@ export const filters = { [OPERATOR_IS_NOT]: 'not[assignee_username][]', }, }, + milestone: { + apiParam: { + [OPERATOR_IS]: 'milestone', + [OPERATOR_IS_NOT]: 'not[milestone]', + }, + urlParam: { + [OPERATOR_IS]: 'milestone_title', + [OPERATOR_IS_NOT]: 'not[milestone_title]', + }, + }, labels: { apiParam: { [OPERATOR_IS]: 'labels', @@ -308,4 +318,20 @@ export const filters = { [OPERATOR_IS_NOT]: 'not[label_name][]', }, }, + my_reaction_emoji: { + apiParam: { + [OPERATOR_IS]: 'my_reaction_emoji', + }, + urlParam: { + [OPERATOR_IS]: 'my_reaction_emoji', + }, + }, + confidential: { + apiParam: { + [OPERATOR_IS]: 'confidential', + }, + urlParam: { + [OPERATOR_IS]: 'confidential', + }, + }, }; diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js index 85b64be771864267b3df512a3e1db856fd1bff09..f7cb384308bdf4cb0f34c3a354265674cf709f99 100644 --- a/app/assets/javascripts/issues_list/index.js +++ b/app/assets/javascripts/issues_list/index.js @@ -73,6 +73,7 @@ export function initIssuesListApp() { } const { + autocompleteAwardEmojisPath, autocompleteUsersPath, calendarPath, canBulkUpdate, @@ -94,6 +95,7 @@ export function initIssuesListApp() { newIssuePath, projectImportJiraPath, projectLabelsPath, + projectMilestonesPath, projectPath, rssPath, showNewIssueLink, @@ -106,6 +108,7 @@ export function initIssuesListApp() { // issue is fixed upstream in https://github.com/vuejs/vue-apollo/pull/1153 apolloProvider: {}, provide: { + autocompleteAwardEmojisPath, autocompleteUsersPath, calendarPath, canBulkUpdate: parseBoolean(canBulkUpdate), @@ -120,6 +123,7 @@ export function initIssuesListApp() { jiraIntegrationPath, newIssuePath, projectLabelsPath, + projectMilestonesPath, projectPath, rssPath, showNewIssueLink: parseBoolean(showNewIssueLink), diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue index 98190d716c9c695d84e02ec8ca2409f7614a0d4a..269e29a6dff07bd91bd276ecc0003c3f61f09ff6 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue @@ -47,6 +47,16 @@ export default { ); }, }, + watch: { + active: { + immediate: true, + handler(newValue) { + if (!newValue && !this.emojis.length) { + this.fetchEmojiBySearchTerm(this.value.data); + } + }, + }, + }, methods: { fetchEmojiBySearchTerm(searchTerm) { this.loading = true; diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 2296fe91fba8e79de7c2141debd2d50c18adc579..754c96ca887bceec55c2a4f8d30b2fe54a621b5c 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -166,6 +166,7 @@ def issue_header_actions_data(project, issuable, current_user) def issues_list_data(project, current_user, finder) { autocomplete_users_path: autocomplete_users_path(active: true, current_user: true, project_id: project.id, format: :json), + autocomplete_award_emojis_path: autocomplete_award_emojis_path, calendar_path: url_for(safe_params.merge(calendar_url_options)), can_bulk_update: can?(current_user, :admin_issue, project).to_s, can_edit: can?(current_user, :admin_project, project).to_s, @@ -183,6 +184,7 @@ def issues_list_data(project, current_user, finder) new_issue_path: new_project_issue_path(project, issue: { assignee_id: finder.assignee.try(:id), milestone_id: finder.milestones.first.try(:id) }), project_import_jira_path: project_import_jira_path(project), project_labels_path: project_labels_path(project, include_ancestor_groups: true, format: :json), + project_milestones_path: project_milestones_path(project, format: :json), project_path: project.full_path, rss_path: url_for(safe_params.merge(rss_url_options)), show_new_issue_link: show_new_issue_link?(project).to_s, diff --git a/spec/frontend/issues_list/components/issues_list_app_spec.js b/spec/frontend/issues_list/components/issues_list_app_spec.js index 65bdcc8d219d8ca2a36baf12fde96433dd299c6d..7d559de507add34d6d0763a4ecbb093cfd28beb6 100644 --- a/spec/frontend/issues_list/components/issues_list_app_spec.js +++ b/spec/frontend/issues_list/components/issues_list_app_spec.js @@ -3,7 +3,7 @@ import { mount, shallowMount } from '@vue/test-utils'; import AxiosMockAdapter from 'axios-mock-adapter'; import { TEST_HOST } from 'helpers/test_constants'; import waitForPromises from 'helpers/wait_for_promises'; -import { filteredTokens, locationSearch } from 'jest/issues_list/mock_data'; +import { apiParams, filteredTokens, locationSearch, urlParams } from 'jest/issues_list/mock_data'; import createFlash from '~/flash'; import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; import IssuableList from '~/issuable_list/components/issuable_list_root.vue'; @@ -558,25 +558,11 @@ describe('IssuesListApp component', () => { }); it('makes an API call to search for issues with the search term', () => { - expect(axiosMock.history.get[1].params).toMatchObject({ - author_username: 'homer', - 'not[author_username]': 'marge', - assignee_username: 'bart', - 'not[assignee_username]': 'lisa', - labels: 'cartoon,tv', - 'not[labels]': 'live action,drama', - }); + expect(axiosMock.history.get[1].params).toMatchObject(apiParams); }); it('updates IssuableList with url params', () => { - expect(findIssuableList().props('urlParams')).toMatchObject({ - author_username: ['homer'], - 'not[author_username]': ['marge'], - 'assignee_username[]': ['bart'], - 'not[assignee_username][]': ['lisa'], - 'label_name[]': ['cartoon', 'tv'], - 'not[label_name][]': ['live action', 'drama'], - }); + expect(findIssuableList().props('urlParams')).toMatchObject(urlParams); }); }); }); diff --git a/spec/frontend/issues_list/mock_data.js b/spec/frontend/issues_list/mock_data.js index e036c26689cde9df23303a2579bcaa9334377cb9..faeab0c244dd88137211d2833357248e5abb7861 100644 --- a/spec/frontend/issues_list/mock_data.js +++ b/spec/frontend/issues_list/mock_data.js @@ -6,10 +6,14 @@ export const locationSearch = [ 'not[author_username]=marge', 'assignee_username[]=bart', 'not[assignee_username][]=lisa', + 'milestone_title=season+4', + 'not[milestone_title]=season+20', 'label_name[]=cartoon', 'label_name[]=tv', 'not[label_name][]=live action', 'not[label_name][]=drama', + 'my_reaction_emoji=thumbsup', + 'confidential=no', ].join('&'); export const filteredTokens = [ @@ -17,10 +21,40 @@ export const filteredTokens = [ { type: 'author_username', value: { data: 'marge', operator: OPERATOR_IS_NOT } }, { type: 'assignee_username', value: { data: 'bart', operator: OPERATOR_IS } }, { type: 'assignee_username', value: { data: 'lisa', operator: OPERATOR_IS_NOT } }, + { type: 'milestone', value: { data: 'season 4', operator: OPERATOR_IS } }, + { type: 'milestone', value: { data: 'season 20', operator: OPERATOR_IS_NOT } }, { type: 'labels', value: { data: 'cartoon', operator: OPERATOR_IS } }, { type: 'labels', value: { data: 'tv', operator: OPERATOR_IS } }, { type: 'labels', value: { data: 'live action', operator: OPERATOR_IS_NOT } }, { type: 'labels', value: { data: 'drama', operator: OPERATOR_IS_NOT } }, + { type: 'my_reaction_emoji', value: { data: 'thumbsup', operator: OPERATOR_IS } }, + { type: 'confidential', value: { data: 'no', operator: OPERATOR_IS } }, { type: 'filtered-search-term', value: { data: 'find' } }, { type: 'filtered-search-term', value: { data: 'issues' } }, ]; + +export const apiParams = { + author_username: 'homer', + 'not[author_username]': 'marge', + assignee_username: 'bart', + 'not[assignee_username]': 'lisa', + milestone: 'season 4', + 'not[milestone]': 'season 20', + labels: 'cartoon,tv', + 'not[labels]': 'live action,drama', + my_reaction_emoji: 'thumbsup', + confidential: 'no', +}; + +export const urlParams = { + author_username: ['homer'], + 'not[author_username]': ['marge'], + 'assignee_username[]': ['bart'], + 'not[assignee_username][]': ['lisa'], + milestone_title: ['season 4'], + 'not[milestone_title]': ['season 20'], + 'label_name[]': ['cartoon', 'tv'], + 'not[label_name][]': ['live action', 'drama'], + my_reaction_emoji: ['thumbsup'], + confidential: ['no'], +}; diff --git a/spec/frontend/issues_list/utils_spec.js b/spec/frontend/issues_list/utils_spec.js index 31e4d4e5f458be59828e3c3513eae7696e7f3963..fbd0a53517489be0bbd0fc6373e4da9e95a08539 100644 --- a/spec/frontend/issues_list/utils_spec.js +++ b/spec/frontend/issues_list/utils_spec.js @@ -1,4 +1,4 @@ -import { filteredTokens, locationSearch } from 'jest/issues_list/mock_data'; +import { apiParams, filteredTokens, locationSearch, urlParams } from 'jest/issues_list/mock_data'; import { sortParams } from '~/issues_list/constants'; import { convertToApiParams, @@ -23,27 +23,13 @@ describe('getFilterTokens', () => { describe('convertToApiParams', () => { it('returns api params given filtered tokens', () => { - expect(convertToApiParams(filteredTokens)).toEqual({ - author_username: 'homer', - 'not[author_username]': 'marge', - assignee_username: 'bart', - 'not[assignee_username]': 'lisa', - labels: 'cartoon,tv', - 'not[labels]': 'live action,drama', - }); + expect(convertToApiParams(filteredTokens)).toEqual(apiParams); }); }); describe('convertToUrlParams', () => { it('returns url params given filtered tokens', () => { - expect(convertToUrlParams(filteredTokens)).toEqual({ - author_username: ['homer'], - 'not[author_username]': ['marge'], - 'assignee_username[]': ['bart'], - 'not[assignee_username][]': ['lisa'], - 'label_name[]': ['cartoon', 'tv'], - 'not[label_name][]': ['live action', 'drama'], - }); + expect(convertToUrlParams(filteredTokens)).toEqual(urlParams); }); }); diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index c4eb69d830725dc5bbe314426f18303518168b50..762c105741b255105486439d939a5fb619eb0490 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -293,6 +293,7 @@ allow(helper).to receive(:url_for).and_return('#') expected = { + autocomplete_award_emojis_path: autocomplete_award_emojis_path, autocomplete_users_path: autocomplete_users_path(active: true, current_user: true, project_id: project.id, format: :json), calendar_path: '#', can_bulk_update: 'true', @@ -311,6 +312,7 @@ new_issue_path: new_project_issue_path(project, issue: { assignee_id: finder.assignee.id, milestone_id: finder.milestones.first.id }), project_import_jira_path: project_import_jira_path(project), project_labels_path: project_labels_path(project, include_ancestor_groups: true, format: :json), + project_milestones_path: project_milestones_path(project, format: :json), project_path: project.full_path, rss_path: '#', show_new_issue_link: 'true',