diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-14 06:08:27 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-14 06:08:27 +0300 |
commit | cd4d8b60a0ab51c6d6075a6b7206f1ddbf6295d3 (patch) | |
tree | 28dac4d2fafb5f88a40d3935a4e6482584e80b58 /app/assets/javascripts | |
parent | 6143ef2fb6772c50fde4e738412c5d511f9dcf15 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
10 files changed, 220 insertions, 8 deletions
diff --git a/app/assets/javascripts/boards/components/board_filtered_search.vue b/app/assets/javascripts/boards/components/board_filtered_search.vue index 13388f02f1f..cfd6b21fa66 100644 --- a/app/assets/javascripts/boards/components/board_filtered_search.vue +++ b/app/assets/javascripts/boards/components/board_filtered_search.vue @@ -27,7 +27,7 @@ export default { }, computed: { urlParams() { - const { authorUsername, labelName, search } = this.filterParams; + const { authorUsername, labelName, assigneeUsername, search } = this.filterParams; let notParams = {}; if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) { @@ -35,6 +35,7 @@ export default { { 'not[label_name][]': this.filterParams.not.labelName, 'not[author_username]': this.filterParams.not.authorUsername, + 'not[assignee_username]': this.filterParams.not.assigneeUsername, }, undefined, ); @@ -44,6 +45,7 @@ export default { ...notParams, author_username: authorUsername, 'label_name[]': labelName, + assignee_username: assigneeUsername, search, }; }, @@ -62,7 +64,7 @@ export default { this.performSearch(); }, getFilteredSearchValue() { - const { authorUsername, labelName, search } = this.filterParams; + const { authorUsername, labelName, assigneeUsername, search } = this.filterParams; const filteredSearchValue = []; if (authorUsername) { @@ -72,6 +74,13 @@ export default { }); } + if (assigneeUsername) { + filteredSearchValue.push({ + type: 'assignee_username', + value: { data: assigneeUsername, operator: '=' }, + }); + } + if (labelName?.length) { filteredSearchValue.push( ...labelName.map((label) => ({ @@ -88,6 +97,13 @@ export default { }); } + if (this.filterParams['not[assigneeUsername]']) { + filteredSearchValue.push({ + type: 'assignee_username', + value: { data: this.filterParams['not[assigneeUsername]'], operator: '!=' }, + }); + } + if (this.filterParams['not[labelName]']) { filteredSearchValue.push( ...this.filterParams['not[labelName]'].map((label) => ({ @@ -121,6 +137,9 @@ export default { case 'author_username': filterParams.authorUsername = filter.value.data; break; + case 'assignee_username': + filterParams.assigneeUsername = filter.value.data; + break; case 'label_name': labels.push(filter.value.data); break; diff --git a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue new file mode 100644 index 00000000000..d8dac17d326 --- /dev/null +++ b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue @@ -0,0 +1,102 @@ +<script> +import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue'; +import issueBoardFilters from '~/boards/issue_board_filters'; +import { TYPE_USER } from '~/graphql_shared/constants'; +import { convertToGraphQLId } from '~/graphql_shared/utils'; +import { __ } from '~/locale'; +import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue'; +import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'; + +export default { + i18n: { + search: __('Search'), + label: __('Label'), + author: __('Author'), + assignee: __('Assignee'), + is: __('is'), + isNot: __('is not'), + }, + components: { BoardFilteredSearch }, + props: { + fullPath: { + type: String, + required: true, + }, + boardType: { + type: String, + required: true, + }, + }, + computed: { + tokens() { + const { label, is, isNot, author, assignee } = this.$options.i18n; + const { fetchAuthors, fetchLabels } = issueBoardFilters( + this.$apollo, + this.fullPath, + this.boardType, + ); + + return [ + { + icon: 'labels', + title: label, + type: 'label_name', + operators: [ + { value: '=', description: is }, + { value: '!=', description: isNot }, + ], + token: LabelToken, + unique: false, + symbol: '~', + fetchLabels, + }, + { + icon: 'pencil', + title: author, + type: 'author_username', + operators: [ + { value: '=', description: is }, + { value: '!=', description: isNot }, + ], + symbol: '@', + token: AuthorToken, + unique: true, + fetchAuthors, + preloadedAuthors: this.preloadedAuthors(), + }, + { + icon: 'user', + title: assignee, + type: 'assignee_username', + operators: [ + { value: '=', description: is }, + { value: '!=', description: isNot }, + ], + token: AuthorToken, + unique: true, + fetchAuthors, + preloadedAuthors: this.preloadedAuthors(), + }, + ]; + }, + }, + methods: { + preloadedAuthors() { + return gon?.current_user_id + ? [ + { + id: convertToGraphQLId(TYPE_USER, gon.current_user_id), + name: gon.current_user_fullname, + username: gon.current_username, + avatarUrl: gon.current_user_avatar_url, + }, + ] + : []; + }, + }, +}; +</script> + +<template> + <board-filtered-search data-testid="issue-board-filtered-search" :tokens="tokens" /> +</template> diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 9416fbb7825..de7c8a3bd6b 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -25,6 +25,7 @@ import '~/boards/filters/due_date_filters'; import { issuableTypes } from '~/boards/constants'; import eventHub from '~/boards/eventhub'; import FilteredSearchBoards from '~/boards/filtered_search_boards'; +import initBoardsFilteredSearch from '~/boards/mount_filtered_search_issue_boards'; import store from '~/boards/stores'; import boardsStore from '~/boards/stores/boards_store'; import toggleFocusMode from '~/boards/toggle_focus'; @@ -78,6 +79,10 @@ export default () => { issueBoardsApp.$destroy(true); } + if (gon?.features?.issueBoardsFilteredSearch) { + initBoardsFilteredSearch(apolloProvider); + } + if (!gon?.features?.graphqlBoardLists) { boardsStore.create(); boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours); @@ -184,9 +189,14 @@ export default () => { eventHub.$off('initialBoardLoad', this.initialBoardLoad); }, mounted() { - this.filterManager = new FilteredSearchBoards(boardsStore.filter, true, boardsStore.cantEdit); - - this.filterManager.setup(); + if (!gon?.features?.issueBoardsFilteredSearch) { + this.filterManager = new FilteredSearchBoards( + boardsStore.filter, + true, + boardsStore.cantEdit, + ); + this.filterManager.setup(); + } this.performSearch(); diff --git a/app/assets/javascripts/boards/issue_board_filters.js b/app/assets/javascripts/boards/issue_board_filters.js new file mode 100644 index 00000000000..699d7e12de4 --- /dev/null +++ b/app/assets/javascripts/boards/issue_board_filters.js @@ -0,0 +1,47 @@ +import groupBoardMembers from '~/boards/graphql/group_board_members.query.graphql'; +import projectBoardMembers from '~/boards/graphql/project_board_members.query.graphql'; +import { BoardType } from './constants'; +import boardLabels from './graphql/board_labels.query.graphql'; + +export default function issueBoardFilters(apollo, fullPath, boardType) { + const isGroupBoard = boardType === BoardType.group; + const isProjectBoard = boardType === BoardType.project; + const transformLabels = ({ data }) => { + return isGroupBoard ? data.group?.labels.nodes || [] : data.project?.labels.nodes || []; + }; + + const boardAssigneesQuery = () => { + return isGroupBoard ? groupBoardMembers : projectBoardMembers; + }; + + const fetchAuthors = (authorsSearchTerm) => { + return apollo + .query({ + query: boardAssigneesQuery(), + variables: { + fullPath, + search: authorsSearchTerm, + }, + }) + .then(({ data }) => data.workspace?.assignees.nodes.map(({ user }) => user)); + }; + + const fetchLabels = (labelSearchTerm) => { + return apollo + .query({ + query: boardLabels, + variables: { + fullPath, + searchTerm: labelSearchTerm, + isGroup: isGroupBoard, + isProject: isProjectBoard, + }, + }) + .then(transformLabels); + }; + + return { + fetchLabels, + fetchAuthors, + }; +} diff --git a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js new file mode 100644 index 00000000000..7732091ef34 --- /dev/null +++ b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js @@ -0,0 +1,31 @@ +import Vue from 'vue'; +import IssueBoardFilteredSearch from '~/boards/components/issue_board_filtered_search.vue'; +import store from '~/boards/stores'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { queryToObject } from '~/lib/utils/url_utility'; + +export default (apolloProvider) => { + const el = document.getElementById('js-issue-board-filtered-search'); + const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true }); + + const initialFilterParams = { + ...convertObjectPropsToCamelCase(rawFilterParams, {}), + }; + + if (!el) { + return null; + } + + return new Vue({ + el, + provide: { + initialFilterParams, + }, + store, // TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/324094 + apolloProvider, + render: (createElement) => + createElement(IssueBoardFilteredSearch, { + props: { fullPath: store.state?.fullPath || '', boardType: store.state?.boardType || '' }, + }), + }); +}; diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index 3cc62cf124c..ad0b27c9693 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -178,7 +178,7 @@ export default { </div> </div> <div v-if="isGroupPendingRemoval"> - <gl-badge variant="warning">{{ __('pending removal') }}</gl-badge> + <gl-badge variant="warning">{{ __('pending deletion') }}</gl-badge> </div> <div class="metadata d-flex flex-grow-1 flex-shrink-0 flex-wrap justify-content-md-between"> <item-actions diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue index e09df8a5d26..7a37d1eb93d 100644 --- a/app/assets/javascripts/groups/components/item_stats.vue +++ b/app/assets/javascripts/groups/components/item_stats.vue @@ -73,7 +73,7 @@ export default { icon-name="star" /> <div v-if="isProjectPendingRemoval"> - <gl-badge variant="warning">{{ __('pending removal') }}</gl-badge> + <gl-badge variant="warning">{{ __('pending deletion') }}</gl-badge> </div> <div v-if="isProject" class="last-updated"> <time-ago-tooltip :time="item.updatedAt" tooltip-placement="bottom" /> diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue index 88f4bba5e2a..d41488acf46 100644 --- a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue +++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue @@ -101,7 +101,7 @@ export default { v-if="isGroupPendingRemoval" variant="warning" class="gl-display-none gl-sm-display-flex gl-mt-3 gl-mr-1" - >{{ __('pending removal') }}</gl-badge + >{{ __('pending deletion') }}</gl-badge > <user-access-role-badge v-if="group.permission" class="gl-mt-3"> {{ group.permission }} diff --git a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue index f6e88738002..f1fe8cf10fd 100644 --- a/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue +++ b/app/assets/javascripts/pipeline_editor/components/commit/commit_form.vue @@ -103,6 +103,7 @@ export default { v-model="targetBranch" class="gl-font-monospace!" required + data-qa-selector="target_branch_field" /> <gl-form-checkbox v-if="!isCurrentBranchTarget" diff --git a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue index e01f9cc79c0..ee6d4ff7c4d 100644 --- a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue +++ b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue @@ -211,6 +211,7 @@ export default { :header-text="$options.i18n.dropdownHeader" :text="currentBranch" icon="branch" + data-qa-selector="branch_selector_button" > <gl-search-box-by-type :debounce="$options.inputDebounce" @input="setSearchTerm" /> <gl-dropdown-section-header> @@ -228,6 +229,7 @@ export default { :key="branch" :is-checked="currentBranch === branch" :is-check-item="true" + data-qa-selector="menu_branch_button" @click="selectBranch(branch)" > {{ branch }} |